9
go.mod
9
go.mod
@@ -7,6 +7,13 @@ require (
|
|||||||
github.com/go-chi/cors v1.2.1
|
github.com/go-chi/cors v1.2.1
|
||||||
github.com/google/uuid v1.3.0
|
github.com/google/uuid v1.3.0
|
||||||
github.com/joho/godotenv v1.5.1
|
github.com/joho/godotenv v1.5.1
|
||||||
|
github.com/tursodatabase/libsql-client-go v0.0.0-20240220085343-4ae0eb9d0898
|
||||||
)
|
)
|
||||||
|
|
||||||
require github.com/go-sql-driver/mysql v1.7.1
|
require (
|
||||||
|
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230512164433-5d1fd1a340c9 // indirect
|
||||||
|
github.com/klauspost/compress v1.15.15 // indirect
|
||||||
|
github.com/libsql/sqlite-antlr4-parser v0.0.0-20230802215326-5cb5bb604475 // indirect
|
||||||
|
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect
|
||||||
|
nhooyr.io/websocket v1.8.7 // indirect
|
||||||
|
)
|
||||||
|
|||||||
71
go.sum
71
go.sum
@@ -1,10 +1,77 @@
|
|||||||
|
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230512164433-5d1fd1a340c9 h1:goHVqTbFX3AIo0tzGr14pgfAW2ZfPChKO21Z9MGf/gk=
|
||||||
|
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230512164433-5d1fd1a340c9/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||||
|
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||||
|
github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
|
||||||
|
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
||||||
github.com/go-chi/chi v1.5.4 h1:QHdzF2szwjqVV4wmByUnTcsbIg7UGaQ0tPF2t5GcAIs=
|
github.com/go-chi/chi v1.5.4 h1:QHdzF2szwjqVV4wmByUnTcsbIg7UGaQ0tPF2t5GcAIs=
|
||||||
github.com/go-chi/chi v1.5.4/go.mod h1:uaf8YgoFazUOkPBG7fxPftUylNumIev9awIWOENIuEg=
|
github.com/go-chi/chi v1.5.4/go.mod h1:uaf8YgoFazUOkPBG7fxPftUylNumIev9awIWOENIuEg=
|
||||||
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
|
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
|
||||||
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
|
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
|
||||||
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
|
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
|
||||||
|
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||||
|
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
|
||||||
|
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||||
|
github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=
|
||||||
|
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
|
||||||
|
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0=
|
||||||
|
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
|
||||||
|
github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8=
|
||||||
|
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||||
|
github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo=
|
||||||
|
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
|
||||||
|
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||||
|
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||||
|
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||||
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||||
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM=
|
||||||
|
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||||
|
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
|
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||||
|
github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw=
|
||||||
|
github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4=
|
||||||
|
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
||||||
|
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||||
|
github.com/libsql/sqlite-antlr4-parser v0.0.0-20230802215326-5cb5bb604475 h1:6PfEMwfInASh9hkN83aR0j4W/eKaAZt/AURtXAXlas0=
|
||||||
|
github.com/libsql/sqlite-antlr4-parser v0.0.0-20230802215326-5cb5bb604475/go.mod h1:20nXSmcf0nAscrzqsXeC2/tA3KkV2eCiJqYuyAgl+ss=
|
||||||
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
|
github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
||||||
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
github.com/tursodatabase/libsql-client-go v0.0.0-20240220085343-4ae0eb9d0898 h1:1MvEhzI5pvP27e9Dzz861mxk9WzXZLSJwzOU67cKTbU=
|
||||||
|
github.com/tursodatabase/libsql-client-go v0.0.0-20240220085343-4ae0eb9d0898/go.mod h1:9bKuHS7eZh/0mJndbUOrCx8Ej3PlsRDszj4L7oVYMPQ=
|
||||||
|
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
||||||
|
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||||
|
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
|
||||||
|
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||||
|
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA=
|
||||||
|
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA=
|
||||||
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
|
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
|
nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g=
|
||||||
|
nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -16,7 +17,14 @@ func (cfg *apiConfig) handlerNotesGet(w http.ResponseWriter, r *http.Request, us
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
respondWithJSON(w, http.StatusOK, databasePostsToPosts(posts))
|
postsResp, err := databasePostsToPosts(posts)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
respondWithError(w, http.StatusInternalServerError, "Couldn't convert posts")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
respondWithJSON(w, http.StatusOK, postsResp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *apiConfig) handlerNotesCreate(w http.ResponseWriter, r *http.Request, user database.User) {
|
func (cfg *apiConfig) handlerNotesCreate(w http.ResponseWriter, r *http.Request, user database.User) {
|
||||||
@@ -34,8 +42,8 @@ func (cfg *apiConfig) handlerNotesCreate(w http.ResponseWriter, r *http.Request,
|
|||||||
id := uuid.New().String()
|
id := uuid.New().String()
|
||||||
err = cfg.DB.CreateNote(r.Context(), database.CreateNoteParams{
|
err = cfg.DB.CreateNote(r.Context(), database.CreateNoteParams{
|
||||||
ID: id,
|
ID: id,
|
||||||
CreatedAt: time.Now().UTC(),
|
CreatedAt: time.Now().UTC().Format(time.RFC3339),
|
||||||
UpdatedAt: time.Now().UTC(),
|
UpdatedAt: time.Now().UTC().Format(time.RFC3339),
|
||||||
Note: params.Note,
|
Note: params.Note,
|
||||||
UserID: user.ID,
|
UserID: user.ID,
|
||||||
})
|
})
|
||||||
@@ -49,5 +57,13 @@ func (cfg *apiConfig) handlerNotesCreate(w http.ResponseWriter, r *http.Request,
|
|||||||
respondWithError(w, http.StatusNotFound, "Couldn't get note")
|
respondWithError(w, http.StatusNotFound, "Couldn't get note")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
respondWithJSON(w, http.StatusCreated, databaseNoteToNote(note))
|
|
||||||
|
noteResp, err := databaseNoteToNote(note)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
respondWithError(w, http.StatusInternalServerError, "Couldn't convert note")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
respondWithJSON(w, http.StatusCreated, noteResp)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,8 +33,8 @@ func (cfg *apiConfig) handlerUsersCreate(w http.ResponseWriter, r *http.Request)
|
|||||||
|
|
||||||
err = cfg.DB.CreateUser(r.Context(), database.CreateUserParams{
|
err = cfg.DB.CreateUser(r.Context(), database.CreateUserParams{
|
||||||
ID: uuid.New().String(),
|
ID: uuid.New().String(),
|
||||||
CreatedAt: time.Now().UTC(),
|
CreatedAt: time.Now().UTC().Format(time.RFC3339),
|
||||||
UpdatedAt: time.Now().UTC(),
|
UpdatedAt: time.Now().UTC().Format(time.RFC3339),
|
||||||
Name: params.Name,
|
Name: params.Name,
|
||||||
ApiKey: apiKey,
|
ApiKey: apiKey,
|
||||||
})
|
})
|
||||||
@@ -51,7 +51,13 @@ func (cfg *apiConfig) handlerUsersCreate(w http.ResponseWriter, r *http.Request)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
respondWithJSON(w, http.StatusCreated, databaseUserToUser(user))
|
userResp, err := databaseUserToUser(user)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
respondWithError(w, http.StatusInternalServerError, "Couldn't convert user")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
respondWithJSON(w, http.StatusCreated, userResp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateRandomSHA256Hash() (string, error) {
|
func generateRandomSHA256Hash() (string, error) {
|
||||||
@@ -66,5 +72,13 @@ func generateRandomSHA256Hash() (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *apiConfig) handlerUsersGet(w http.ResponseWriter, r *http.Request, user database.User) {
|
func (cfg *apiConfig) handlerUsersGet(w http.ResponseWriter, r *http.Request, user database.User) {
|
||||||
respondWithJSON(w, http.StatusOK, databaseUserToUser(user))
|
|
||||||
|
userResp, err := databaseUserToUser(user)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
respondWithError(w, http.StatusInternalServerError, "Couldn't convert user")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
respondWithJSON(w, http.StatusOK, userResp)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.18.0
|
// sqlc v1.25.0
|
||||||
|
|
||||||
package database
|
package database
|
||||||
|
|
||||||
|
|||||||
@@ -1,25 +1,23 @@
|
|||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.18.0
|
// sqlc v1.25.0
|
||||||
|
|
||||||
package database
|
package database
|
||||||
|
|
||||||
import (
|
import ()
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Note struct {
|
type Note struct {
|
||||||
ID string
|
ID string
|
||||||
CreatedAt time.Time
|
CreatedAt string
|
||||||
UpdatedAt time.Time
|
UpdatedAt string
|
||||||
Note string
|
Note string
|
||||||
UserID string
|
UserID string
|
||||||
}
|
}
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
ID string
|
ID string
|
||||||
CreatedAt time.Time
|
CreatedAt string
|
||||||
UpdatedAt time.Time
|
UpdatedAt string
|
||||||
Name string
|
Name string
|
||||||
ApiKey string
|
ApiKey string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.18.0
|
// sqlc v1.25.0
|
||||||
// source: notes.sql
|
// source: notes.sql
|
||||||
|
|
||||||
package database
|
package database
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const createNote = `-- name: CreateNote :exec
|
const createNote = `-- name: CreateNote :exec
|
||||||
@@ -17,8 +16,8 @@ VALUES (?, ?, ?, ?, ?)
|
|||||||
|
|
||||||
type CreateNoteParams struct {
|
type CreateNoteParams struct {
|
||||||
ID string
|
ID string
|
||||||
CreatedAt time.Time
|
CreatedAt string
|
||||||
UpdatedAt time.Time
|
UpdatedAt string
|
||||||
Note string
|
Note string
|
||||||
UserID string
|
UserID string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
// Code generated by sqlc. DO NOT EDIT.
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
// versions:
|
// versions:
|
||||||
// sqlc v1.18.0
|
// sqlc v1.25.0
|
||||||
// source: users.sql
|
// source: users.sql
|
||||||
|
|
||||||
package database
|
package database
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const createUser = `-- name: CreateUser :exec
|
const createUser = `-- name: CreateUser :exec
|
||||||
@@ -23,8 +22,8 @@ VALUES (
|
|||||||
|
|
||||||
type CreateUserParams struct {
|
type CreateUserParams struct {
|
||||||
ID string
|
ID string
|
||||||
CreatedAt time.Time
|
CreatedAt string
|
||||||
UpdatedAt time.Time
|
UpdatedAt string
|
||||||
Name string
|
Name string
|
||||||
ApiKey string
|
ApiKey string
|
||||||
}
|
}
|
||||||
|
|||||||
29
main.go
29
main.go
@@ -6,9 +6,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/go-chi/chi"
|
"github.com/go-chi/chi"
|
||||||
"github.com/go-chi/cors"
|
"github.com/go-chi/cors"
|
||||||
@@ -16,7 +14,7 @@ import (
|
|||||||
|
|
||||||
"github.com/bootdotdev/learn-cicd-starter/internal/database"
|
"github.com/bootdotdev/learn-cicd-starter/internal/database"
|
||||||
|
|
||||||
_ "github.com/go-sql-driver/mysql"
|
_ "github.com/tursodatabase/libsql-client-go/libsql"
|
||||||
)
|
)
|
||||||
|
|
||||||
type apiConfig struct {
|
type apiConfig struct {
|
||||||
@@ -39,16 +37,14 @@ func main() {
|
|||||||
|
|
||||||
apiCfg := apiConfig{}
|
apiCfg := apiConfig{}
|
||||||
|
|
||||||
|
// https://github.com/libsql/libsql-client-go/#open-a-connection-to-sqld
|
||||||
|
// libsql://[your-database].turso.io?authToken=[your-auth-token]
|
||||||
dbURL := os.Getenv("DATABASE_URL")
|
dbURL := os.Getenv("DATABASE_URL")
|
||||||
if dbURL == "" {
|
if dbURL == "" {
|
||||||
log.Println("DATABASE_URL environment variable is not set")
|
log.Println("DATABASE_URL environment variable is not set")
|
||||||
log.Println("Running without CRUD endpoints")
|
log.Println("Running without CRUD endpoints")
|
||||||
} else {
|
} else {
|
||||||
parsedURL, err := addParseTimeParam(dbURL)
|
db, err := sql.Open("libsql", dbURL)
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
db, err := sql.Open("mysql", parsedURL)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -100,20 +96,3 @@ func main() {
|
|||||||
log.Printf("Serving on port: %s\n", port)
|
log.Printf("Serving on port: %s\n", port)
|
||||||
log.Fatal(srv.ListenAndServe())
|
log.Fatal(srv.ListenAndServe())
|
||||||
}
|
}
|
||||||
|
|
||||||
func addParseTimeParam(input string) (string, error) {
|
|
||||||
const dummyScheme = "http://"
|
|
||||||
if !strings.Contains(input, dummyScheme) {
|
|
||||||
input = "http://" + input
|
|
||||||
}
|
|
||||||
u, err := url.Parse(input)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
q := u.Query()
|
|
||||||
q.Add("parseTime", "true")
|
|
||||||
u.RawQuery = q.Encode()
|
|
||||||
returnUrl := u.String()
|
|
||||||
returnUrl = strings.TrimPrefix(returnUrl, dummyScheme)
|
|
||||||
return returnUrl, nil
|
|
||||||
}
|
|
||||||
|
|||||||
45
models.go
45
models.go
@@ -14,14 +14,23 @@ type User struct {
|
|||||||
ApiKey string `json:"api_key"`
|
ApiKey string `json:"api_key"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func databaseUserToUser(user database.User) User {
|
func databaseUserToUser(user database.User) (User, error) {
|
||||||
|
createdAt, err := time.Parse(time.RFC3339, user.CreatedAt)
|
||||||
|
if err != nil {
|
||||||
|
return User{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
updatedAt, err := time.Parse(time.RFC3339, user.UpdatedAt)
|
||||||
|
if err != nil {
|
||||||
|
return User{}, err
|
||||||
|
}
|
||||||
return User{
|
return User{
|
||||||
ID: user.ID,
|
ID: user.ID,
|
||||||
CreatedAt: user.CreatedAt,
|
CreatedAt: createdAt,
|
||||||
UpdatedAt: user.UpdatedAt,
|
UpdatedAt: updatedAt,
|
||||||
Name: user.Name,
|
Name: user.Name,
|
||||||
ApiKey: user.ApiKey,
|
ApiKey: user.ApiKey,
|
||||||
}
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type Note struct {
|
type Note struct {
|
||||||
@@ -32,20 +41,34 @@ type Note struct {
|
|||||||
UserID string `json:"user_id"`
|
UserID string `json:"user_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func databaseNoteToNote(post database.Note) Note {
|
func databaseNoteToNote(post database.Note) (Note, error) {
|
||||||
|
createdAt, err := time.Parse(time.RFC3339, post.CreatedAt)
|
||||||
|
if err != nil {
|
||||||
|
return Note{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
updatedAt, err := time.Parse(time.RFC3339, post.UpdatedAt)
|
||||||
|
if err != nil {
|
||||||
|
return Note{}, err
|
||||||
|
}
|
||||||
return Note{
|
return Note{
|
||||||
ID: post.ID,
|
ID: post.ID,
|
||||||
CreatedAt: post.CreatedAt,
|
CreatedAt: createdAt,
|
||||||
UpdatedAt: post.UpdatedAt,
|
UpdatedAt: updatedAt,
|
||||||
Note: post.Note,
|
Note: post.Note,
|
||||||
UserID: post.UserID,
|
UserID: post.UserID,
|
||||||
}
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func databasePostsToPosts(notes []database.Note) []Note {
|
func databasePostsToPosts(notes []database.Note) ([]Note, error) {
|
||||||
result := make([]Note, len(notes))
|
result := make([]Note, len(notes))
|
||||||
for i, note := range notes {
|
for i, note := range notes {
|
||||||
result[i] = databaseNoteToNote(note)
|
var err error
|
||||||
|
result[i], err = databaseNoteToNote(note)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
return result
|
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,4 +5,4 @@ if [ -f .env ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
cd sql/schema
|
cd sql/schema
|
||||||
goose mysql $DATABASE_URL up
|
goose turso $DATABASE_URL up
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
-- +goose Up
|
-- +goose Up
|
||||||
CREATE TABLE users (
|
CREATE TABLE users (
|
||||||
id VARCHAR(36) PRIMARY KEY,
|
id TEXT PRIMARY KEY,
|
||||||
created_at TIMESTAMP NOT NULL,
|
created_at TEXT NOT NULL,
|
||||||
updated_at TIMESTAMP NOT NULL,
|
updated_at TEXT NOT NULL,
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
api_key VARCHAR(64) UNIQUE NOT NULL
|
api_key TEXT UNIQUE NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
-- +goose Down
|
-- +goose Down
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
-- +goose Up
|
-- +goose Up
|
||||||
CREATE TABLE notes (
|
CREATE TABLE notes (
|
||||||
id VARCHAR(36) PRIMARY KEY,
|
id TEXT PRIMARY KEY,
|
||||||
created_at TIMESTAMP NOT NULL,
|
created_at TEXT NOT NULL,
|
||||||
updated_at TIMESTAMP NOT NULL,
|
updated_at TEXT NOT NULL,
|
||||||
note TEXT NOT NULL,
|
note TEXT NOT NULL,
|
||||||
user_id VARCHAR(36) NOT NULL REFERENCES users(id) ON DELETE CASCADE
|
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
|
|
||||||
-- +goose Down
|
-- +goose Down
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ version: "2"
|
|||||||
sql:
|
sql:
|
||||||
- schema: "sql/schema"
|
- schema: "sql/schema"
|
||||||
queries: "sql/queries"
|
queries: "sql/queries"
|
||||||
engine: "mysql"
|
engine: "sqlite"
|
||||||
gen:
|
gen:
|
||||||
go:
|
go:
|
||||||
out: "internal/database"
|
out: "internal/database"
|
||||||
|
|||||||
26
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/LICENSE
generated
vendored
Normal file
26
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
Copyright 2021 The ANTLR Project
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
1. Redistributions of source code must retain the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
3. Neither the name of the copyright holder nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from this
|
||||||
|
software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
68
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/antlrdoc.go
generated
vendored
Normal file
68
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/antlrdoc.go
generated
vendored
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
Package antlr implements the Go version of the ANTLR 4 runtime.
|
||||||
|
|
||||||
|
# The ANTLR Tool
|
||||||
|
|
||||||
|
ANTLR (ANother Tool for Language Recognition) is a powerful parser generator for reading, processing, executing,
|
||||||
|
or translating structured text or binary files. It's widely used to build languages, tools, and frameworks.
|
||||||
|
From a grammar, ANTLR generates a parser that can build parse trees and also generates a listener interface
|
||||||
|
(or visitor) that makes it easy to respond to the recognition of phrases of interest.
|
||||||
|
|
||||||
|
# Code Generation
|
||||||
|
|
||||||
|
ANTLR supports the generation of code in a number of [target languages], and the generated code is supported by a
|
||||||
|
runtime library, written specifically to support the generated code in the target language. This library is the
|
||||||
|
runtime for the Go target.
|
||||||
|
|
||||||
|
To generate code for the go target, it is generally recommended to place the source grammar files in a package of
|
||||||
|
their own, and use the `.sh` script method of generating code, using the go generate directive. In that same directory
|
||||||
|
it is usual, though not required, to place the antlr tool that should be used to generate the code. That does mean
|
||||||
|
that the antlr tool JAR file will be checked in to your source code control though, so you are free to use any other
|
||||||
|
way of specifying the version of the ANTLR tool to use, such as aliasing in `.zshrc` or equivalent, or a profile in
|
||||||
|
your IDE, or configuration in your CI system.
|
||||||
|
|
||||||
|
Here is a general template for an ANTLR based recognizer in Go:
|
||||||
|
|
||||||
|
.
|
||||||
|
├── myproject
|
||||||
|
├── parser
|
||||||
|
│ ├── mygrammar.g4
|
||||||
|
│ ├── antlr-4.12.0-complete.jar
|
||||||
|
│ ├── error_listeners.go
|
||||||
|
│ ├── generate.go
|
||||||
|
│ ├── generate.sh
|
||||||
|
├── go.mod
|
||||||
|
├── go.sum
|
||||||
|
├── main.go
|
||||||
|
└── main_test.go
|
||||||
|
|
||||||
|
Make sure that the package statement in your grammar file(s) reflects the go package they exist in.
|
||||||
|
The generate.go file then looks like this:
|
||||||
|
|
||||||
|
package parser
|
||||||
|
|
||||||
|
//go:generate ./generate.sh
|
||||||
|
|
||||||
|
And the generate.sh file will look similar to this:
|
||||||
|
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
alias antlr4='java -Xmx500M -cp "./antlr4-4.12.0-complete.jar:$CLASSPATH" org.antlr.v4.Tool'
|
||||||
|
antlr4 -Dlanguage=Go -no-visitor -package parser *.g4
|
||||||
|
|
||||||
|
depending on whether you want visitors or listeners or any other ANTLR options.
|
||||||
|
|
||||||
|
From the command line at the root of your package “myproject” you can then simply issue the command:
|
||||||
|
|
||||||
|
go generate ./...
|
||||||
|
|
||||||
|
# Copyright Notice
|
||||||
|
|
||||||
|
Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
||||||
|
|
||||||
|
Use of this file is governed by the BSD 3-clause license, which can be found in the [LICENSE.txt] file in the project root.
|
||||||
|
|
||||||
|
[target languages]: https://github.com/antlr/antlr4/tree/master/runtime
|
||||||
|
[LICENSE.txt]: https://github.com/antlr/antlr4/blob/master/LICENSE.txt
|
||||||
|
*/
|
||||||
|
package antlr
|
||||||
176
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/atn.go
generated
vendored
Normal file
176
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/atn.go
generated
vendored
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
||||||
|
// Use of this file is governed by the BSD 3-clause license that
|
||||||
|
// can be found in the LICENSE.txt file in the project root.
|
||||||
|
|
||||||
|
package antlr
|
||||||
|
|
||||||
|
import "sync"
|
||||||
|
|
||||||
|
// ATNInvalidAltNumber is used to represent an ALT number that has yet to be calculated or
|
||||||
|
// which is invalid for a particular struct such as [*antlr.BaseRuleContext]
|
||||||
|
var ATNInvalidAltNumber int
|
||||||
|
|
||||||
|
// ATN represents an “[Augmented Transition Network]”, though general in ANTLR the term
|
||||||
|
// “Augmented Recursive Transition Network” though there are some descriptions of “[Recursive Transition Network]”
|
||||||
|
// in existence.
|
||||||
|
//
|
||||||
|
// ATNs represent the main networks in the system and are serialized by the code generator and support [ALL(*)].
|
||||||
|
//
|
||||||
|
// [Augmented Transition Network]: https://en.wikipedia.org/wiki/Augmented_transition_network
|
||||||
|
// [ALL(*)]: https://www.antlr.org/papers/allstar-techreport.pdf
|
||||||
|
// [Recursive Transition Network]: https://en.wikipedia.org/wiki/Recursive_transition_network
|
||||||
|
type ATN struct {
|
||||||
|
// DecisionToState is the decision points for all rules, subrules, optional
|
||||||
|
// blocks, ()+, ()*, etc. Each subrule/rule is a decision point, and we must track them so we
|
||||||
|
// can go back later and build DFA predictors for them. This includes
|
||||||
|
// all the rules, subrules, optional blocks, ()+, ()* etc...
|
||||||
|
DecisionToState []DecisionState
|
||||||
|
|
||||||
|
// grammarType is the ATN type and is used for deserializing ATNs from strings.
|
||||||
|
grammarType int
|
||||||
|
|
||||||
|
// lexerActions is referenced by action transitions in the ATN for lexer ATNs.
|
||||||
|
lexerActions []LexerAction
|
||||||
|
|
||||||
|
// maxTokenType is the maximum value for any symbol recognized by a transition in the ATN.
|
||||||
|
maxTokenType int
|
||||||
|
|
||||||
|
modeNameToStartState map[string]*TokensStartState
|
||||||
|
|
||||||
|
modeToStartState []*TokensStartState
|
||||||
|
|
||||||
|
// ruleToStartState maps from rule index to starting state number.
|
||||||
|
ruleToStartState []*RuleStartState
|
||||||
|
|
||||||
|
// ruleToStopState maps from rule index to stop state number.
|
||||||
|
ruleToStopState []*RuleStopState
|
||||||
|
|
||||||
|
// ruleToTokenType maps the rule index to the resulting token type for lexer
|
||||||
|
// ATNs. For parser ATNs, it maps the rule index to the generated bypass token
|
||||||
|
// type if ATNDeserializationOptions.isGenerateRuleBypassTransitions was
|
||||||
|
// specified, and otherwise is nil.
|
||||||
|
ruleToTokenType []int
|
||||||
|
|
||||||
|
states []ATNState
|
||||||
|
|
||||||
|
mu sync.Mutex
|
||||||
|
stateMu sync.RWMutex
|
||||||
|
edgeMu sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewATN returns a new ATN struct representing the given grammarType and is used
|
||||||
|
// for runtime deserialization of ATNs from the code generated by the ANTLR tool
|
||||||
|
func NewATN(grammarType int, maxTokenType int) *ATN {
|
||||||
|
return &ATN{
|
||||||
|
grammarType: grammarType,
|
||||||
|
maxTokenType: maxTokenType,
|
||||||
|
modeNameToStartState: make(map[string]*TokensStartState),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextTokensInContext computes and returns the set of valid tokens that can occur starting
|
||||||
|
// in state s. If ctx is nil, the set of tokens will not include what can follow
|
||||||
|
// the rule surrounding s. In other words, the set will be restricted to tokens
|
||||||
|
// reachable staying within the rule of s.
|
||||||
|
func (a *ATN) NextTokensInContext(s ATNState, ctx RuleContext) *IntervalSet {
|
||||||
|
return NewLL1Analyzer(a).Look(s, nil, ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextTokensNoContext computes and returns the set of valid tokens that can occur starting
|
||||||
|
// in state s and staying in same rule. [antlr.Token.EPSILON] is in set if we reach end of
|
||||||
|
// rule.
|
||||||
|
func (a *ATN) NextTokensNoContext(s ATNState) *IntervalSet {
|
||||||
|
a.mu.Lock()
|
||||||
|
defer a.mu.Unlock()
|
||||||
|
iset := s.GetNextTokenWithinRule()
|
||||||
|
if iset == nil {
|
||||||
|
iset = a.NextTokensInContext(s, nil)
|
||||||
|
iset.readOnly = true
|
||||||
|
s.SetNextTokenWithinRule(iset)
|
||||||
|
}
|
||||||
|
return iset
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextTokens computes and returns the set of valid tokens starting in state s, by
|
||||||
|
// calling either [NextTokensNoContext] (ctx == nil) or [NextTokensInContext] (ctx != nil).
|
||||||
|
func (a *ATN) NextTokens(s ATNState, ctx RuleContext) *IntervalSet {
|
||||||
|
if ctx == nil {
|
||||||
|
return a.NextTokensNoContext(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
return a.NextTokensInContext(s, ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ATN) addState(state ATNState) {
|
||||||
|
if state != nil {
|
||||||
|
state.SetATN(a)
|
||||||
|
state.SetStateNumber(len(a.states))
|
||||||
|
}
|
||||||
|
|
||||||
|
a.states = append(a.states, state)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ATN) removeState(state ATNState) {
|
||||||
|
a.states[state.GetStateNumber()] = nil // Just free the memory; don't shift states in the slice
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ATN) defineDecisionState(s DecisionState) int {
|
||||||
|
a.DecisionToState = append(a.DecisionToState, s)
|
||||||
|
s.setDecision(len(a.DecisionToState) - 1)
|
||||||
|
|
||||||
|
return s.getDecision()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ATN) getDecisionState(decision int) DecisionState {
|
||||||
|
if len(a.DecisionToState) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return a.DecisionToState[decision]
|
||||||
|
}
|
||||||
|
|
||||||
|
// getExpectedTokens computes the set of input symbols which could follow ATN
|
||||||
|
// state number stateNumber in the specified full parse context ctx and returns
|
||||||
|
// the set of potentially valid input symbols which could follow the specified
|
||||||
|
// state in the specified context. This method considers the complete parser
|
||||||
|
// context, but does not evaluate semantic predicates (i.e. all predicates
|
||||||
|
// encountered during the calculation are assumed true). If a path in the ATN
|
||||||
|
// exists from the starting state to the RuleStopState of the outermost context
|
||||||
|
// without Matching any symbols, Token.EOF is added to the returned set.
|
||||||
|
//
|
||||||
|
// A nil ctx defaults to ParserRuleContext.EMPTY.
|
||||||
|
//
|
||||||
|
// It panics if the ATN does not contain state stateNumber.
|
||||||
|
func (a *ATN) getExpectedTokens(stateNumber int, ctx RuleContext) *IntervalSet {
|
||||||
|
if stateNumber < 0 || stateNumber >= len(a.states) {
|
||||||
|
panic("Invalid state number.")
|
||||||
|
}
|
||||||
|
|
||||||
|
s := a.states[stateNumber]
|
||||||
|
following := a.NextTokens(s, nil)
|
||||||
|
|
||||||
|
if !following.contains(TokenEpsilon) {
|
||||||
|
return following
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := NewIntervalSet()
|
||||||
|
|
||||||
|
expected.addSet(following)
|
||||||
|
expected.removeOne(TokenEpsilon)
|
||||||
|
|
||||||
|
for ctx != nil && ctx.GetInvokingState() >= 0 && following.contains(TokenEpsilon) {
|
||||||
|
invokingState := a.states[ctx.GetInvokingState()]
|
||||||
|
rt := invokingState.GetTransitions()[0]
|
||||||
|
|
||||||
|
following = a.NextTokens(rt.(*RuleTransition).followState, nil)
|
||||||
|
expected.addSet(following)
|
||||||
|
expected.removeOne(TokenEpsilon)
|
||||||
|
ctx = ctx.GetParent().(RuleContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
if following.contains(TokenEpsilon) {
|
||||||
|
expected.addOne(TokenEOF)
|
||||||
|
}
|
||||||
|
|
||||||
|
return expected
|
||||||
|
}
|
||||||
303
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/atn_config.go
generated
vendored
Normal file
303
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/atn_config.go
generated
vendored
Normal file
@@ -0,0 +1,303 @@
|
|||||||
|
// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
||||||
|
// Use of this file is governed by the BSD 3-clause license that
|
||||||
|
// can be found in the LICENSE.txt file in the project root.
|
||||||
|
|
||||||
|
package antlr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ATNConfig is a tuple: (ATN state, predicted alt, syntactic, semantic
|
||||||
|
// context). The syntactic context is a graph-structured stack node whose
|
||||||
|
// path(s) to the root is the rule invocation(s) chain used to arrive at the
|
||||||
|
// state. The semantic context is the tree of semantic predicates encountered
|
||||||
|
// before reaching an ATN state.
|
||||||
|
type ATNConfig interface {
|
||||||
|
Equals(o Collectable[ATNConfig]) bool
|
||||||
|
Hash() int
|
||||||
|
|
||||||
|
GetState() ATNState
|
||||||
|
GetAlt() int
|
||||||
|
GetSemanticContext() SemanticContext
|
||||||
|
|
||||||
|
GetContext() PredictionContext
|
||||||
|
SetContext(PredictionContext)
|
||||||
|
|
||||||
|
GetReachesIntoOuterContext() int
|
||||||
|
SetReachesIntoOuterContext(int)
|
||||||
|
|
||||||
|
String() string
|
||||||
|
|
||||||
|
getPrecedenceFilterSuppressed() bool
|
||||||
|
setPrecedenceFilterSuppressed(bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
type BaseATNConfig struct {
|
||||||
|
precedenceFilterSuppressed bool
|
||||||
|
state ATNState
|
||||||
|
alt int
|
||||||
|
context PredictionContext
|
||||||
|
semanticContext SemanticContext
|
||||||
|
reachesIntoOuterContext int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBaseATNConfig7(old *BaseATNConfig) ATNConfig { // TODO: Dup
|
||||||
|
return &BaseATNConfig{
|
||||||
|
state: old.state,
|
||||||
|
alt: old.alt,
|
||||||
|
context: old.context,
|
||||||
|
semanticContext: old.semanticContext,
|
||||||
|
reachesIntoOuterContext: old.reachesIntoOuterContext,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBaseATNConfig6(state ATNState, alt int, context PredictionContext) *BaseATNConfig {
|
||||||
|
return NewBaseATNConfig5(state, alt, context, SemanticContextNone)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBaseATNConfig5(state ATNState, alt int, context PredictionContext, semanticContext SemanticContext) *BaseATNConfig {
|
||||||
|
if semanticContext == nil {
|
||||||
|
panic("semanticContext cannot be nil") // TODO: Necessary?
|
||||||
|
}
|
||||||
|
|
||||||
|
return &BaseATNConfig{state: state, alt: alt, context: context, semanticContext: semanticContext}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBaseATNConfig4(c ATNConfig, state ATNState) *BaseATNConfig {
|
||||||
|
return NewBaseATNConfig(c, state, c.GetContext(), c.GetSemanticContext())
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBaseATNConfig3(c ATNConfig, state ATNState, semanticContext SemanticContext) *BaseATNConfig {
|
||||||
|
return NewBaseATNConfig(c, state, c.GetContext(), semanticContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBaseATNConfig2(c ATNConfig, semanticContext SemanticContext) *BaseATNConfig {
|
||||||
|
return NewBaseATNConfig(c, c.GetState(), c.GetContext(), semanticContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBaseATNConfig1(c ATNConfig, state ATNState, context PredictionContext) *BaseATNConfig {
|
||||||
|
return NewBaseATNConfig(c, state, context, c.GetSemanticContext())
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBaseATNConfig(c ATNConfig, state ATNState, context PredictionContext, semanticContext SemanticContext) *BaseATNConfig {
|
||||||
|
if semanticContext == nil {
|
||||||
|
panic("semanticContext cannot be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &BaseATNConfig{
|
||||||
|
state: state,
|
||||||
|
alt: c.GetAlt(),
|
||||||
|
context: context,
|
||||||
|
semanticContext: semanticContext,
|
||||||
|
reachesIntoOuterContext: c.GetReachesIntoOuterContext(),
|
||||||
|
precedenceFilterSuppressed: c.getPrecedenceFilterSuppressed(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseATNConfig) getPrecedenceFilterSuppressed() bool {
|
||||||
|
return b.precedenceFilterSuppressed
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseATNConfig) setPrecedenceFilterSuppressed(v bool) {
|
||||||
|
b.precedenceFilterSuppressed = v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseATNConfig) GetState() ATNState {
|
||||||
|
return b.state
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseATNConfig) GetAlt() int {
|
||||||
|
return b.alt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseATNConfig) SetContext(v PredictionContext) {
|
||||||
|
b.context = v
|
||||||
|
}
|
||||||
|
func (b *BaseATNConfig) GetContext() PredictionContext {
|
||||||
|
return b.context
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseATNConfig) GetSemanticContext() SemanticContext {
|
||||||
|
return b.semanticContext
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseATNConfig) GetReachesIntoOuterContext() int {
|
||||||
|
return b.reachesIntoOuterContext
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseATNConfig) SetReachesIntoOuterContext(v int) {
|
||||||
|
b.reachesIntoOuterContext = v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equals is the default comparison function for an ATNConfig when no specialist implementation is required
|
||||||
|
// for a collection.
|
||||||
|
//
|
||||||
|
// An ATN configuration is equal to another if both have the same state, they
|
||||||
|
// predict the same alternative, and syntactic/semantic contexts are the same.
|
||||||
|
func (b *BaseATNConfig) Equals(o Collectable[ATNConfig]) bool {
|
||||||
|
if b == o {
|
||||||
|
return true
|
||||||
|
} else if o == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var other, ok = o.(*BaseATNConfig)
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var equal bool
|
||||||
|
|
||||||
|
if b.context == nil {
|
||||||
|
equal = other.context == nil
|
||||||
|
} else {
|
||||||
|
equal = b.context.Equals(other.context)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
nums = b.state.GetStateNumber() == other.state.GetStateNumber()
|
||||||
|
alts = b.alt == other.alt
|
||||||
|
cons = b.semanticContext.Equals(other.semanticContext)
|
||||||
|
sups = b.precedenceFilterSuppressed == other.precedenceFilterSuppressed
|
||||||
|
)
|
||||||
|
|
||||||
|
return nums && alts && cons && sups && equal
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash is the default hash function for BaseATNConfig, when no specialist hash function
|
||||||
|
// is required for a collection
|
||||||
|
func (b *BaseATNConfig) Hash() int {
|
||||||
|
var c int
|
||||||
|
if b.context != nil {
|
||||||
|
c = b.context.Hash()
|
||||||
|
}
|
||||||
|
|
||||||
|
h := murmurInit(7)
|
||||||
|
h = murmurUpdate(h, b.state.GetStateNumber())
|
||||||
|
h = murmurUpdate(h, b.alt)
|
||||||
|
h = murmurUpdate(h, c)
|
||||||
|
h = murmurUpdate(h, b.semanticContext.Hash())
|
||||||
|
return murmurFinish(h, 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseATNConfig) String() string {
|
||||||
|
var s1, s2, s3 string
|
||||||
|
|
||||||
|
if b.context != nil {
|
||||||
|
s1 = ",[" + fmt.Sprint(b.context) + "]"
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.semanticContext != SemanticContextNone {
|
||||||
|
s2 = "," + fmt.Sprint(b.semanticContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.reachesIntoOuterContext > 0 {
|
||||||
|
s3 = ",up=" + fmt.Sprint(b.reachesIntoOuterContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("(%v,%v%v%v%v)", b.state, b.alt, s1, s2, s3)
|
||||||
|
}
|
||||||
|
|
||||||
|
type LexerATNConfig struct {
|
||||||
|
*BaseATNConfig
|
||||||
|
lexerActionExecutor *LexerActionExecutor
|
||||||
|
passedThroughNonGreedyDecision bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLexerATNConfig6(state ATNState, alt int, context PredictionContext) *LexerATNConfig {
|
||||||
|
return &LexerATNConfig{BaseATNConfig: NewBaseATNConfig5(state, alt, context, SemanticContextNone)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLexerATNConfig5(state ATNState, alt int, context PredictionContext, lexerActionExecutor *LexerActionExecutor) *LexerATNConfig {
|
||||||
|
return &LexerATNConfig{
|
||||||
|
BaseATNConfig: NewBaseATNConfig5(state, alt, context, SemanticContextNone),
|
||||||
|
lexerActionExecutor: lexerActionExecutor,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLexerATNConfig4(c *LexerATNConfig, state ATNState) *LexerATNConfig {
|
||||||
|
return &LexerATNConfig{
|
||||||
|
BaseATNConfig: NewBaseATNConfig(c, state, c.GetContext(), c.GetSemanticContext()),
|
||||||
|
lexerActionExecutor: c.lexerActionExecutor,
|
||||||
|
passedThroughNonGreedyDecision: checkNonGreedyDecision(c, state),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLexerATNConfig3(c *LexerATNConfig, state ATNState, lexerActionExecutor *LexerActionExecutor) *LexerATNConfig {
|
||||||
|
return &LexerATNConfig{
|
||||||
|
BaseATNConfig: NewBaseATNConfig(c, state, c.GetContext(), c.GetSemanticContext()),
|
||||||
|
lexerActionExecutor: lexerActionExecutor,
|
||||||
|
passedThroughNonGreedyDecision: checkNonGreedyDecision(c, state),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLexerATNConfig2(c *LexerATNConfig, state ATNState, context PredictionContext) *LexerATNConfig {
|
||||||
|
return &LexerATNConfig{
|
||||||
|
BaseATNConfig: NewBaseATNConfig(c, state, context, c.GetSemanticContext()),
|
||||||
|
lexerActionExecutor: c.lexerActionExecutor,
|
||||||
|
passedThroughNonGreedyDecision: checkNonGreedyDecision(c, state),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLexerATNConfig1(state ATNState, alt int, context PredictionContext) *LexerATNConfig {
|
||||||
|
return &LexerATNConfig{BaseATNConfig: NewBaseATNConfig5(state, alt, context, SemanticContextNone)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash is the default hash function for LexerATNConfig objects, it can be used directly or via
|
||||||
|
// the default comparator [ObjEqComparator].
|
||||||
|
func (l *LexerATNConfig) Hash() int {
|
||||||
|
var f int
|
||||||
|
if l.passedThroughNonGreedyDecision {
|
||||||
|
f = 1
|
||||||
|
} else {
|
||||||
|
f = 0
|
||||||
|
}
|
||||||
|
h := murmurInit(7)
|
||||||
|
h = murmurUpdate(h, l.state.GetStateNumber())
|
||||||
|
h = murmurUpdate(h, l.alt)
|
||||||
|
h = murmurUpdate(h, l.context.Hash())
|
||||||
|
h = murmurUpdate(h, l.semanticContext.Hash())
|
||||||
|
h = murmurUpdate(h, f)
|
||||||
|
h = murmurUpdate(h, l.lexerActionExecutor.Hash())
|
||||||
|
h = murmurFinish(h, 6)
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equals is the default comparison function for LexerATNConfig objects, it can be used directly or via
|
||||||
|
// the default comparator [ObjEqComparator].
|
||||||
|
func (l *LexerATNConfig) Equals(other Collectable[ATNConfig]) bool {
|
||||||
|
if l == other {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
var othert, ok = other.(*LexerATNConfig)
|
||||||
|
|
||||||
|
if l == other {
|
||||||
|
return true
|
||||||
|
} else if !ok {
|
||||||
|
return false
|
||||||
|
} else if l.passedThroughNonGreedyDecision != othert.passedThroughNonGreedyDecision {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var b bool
|
||||||
|
|
||||||
|
if l.lexerActionExecutor != nil {
|
||||||
|
b = !l.lexerActionExecutor.Equals(othert.lexerActionExecutor)
|
||||||
|
} else {
|
||||||
|
b = othert.lexerActionExecutor != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if b {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return l.BaseATNConfig.Equals(othert.BaseATNConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkNonGreedyDecision(source *LexerATNConfig, target ATNState) bool {
|
||||||
|
var ds, ok = target.(DecisionState)
|
||||||
|
|
||||||
|
return source.passedThroughNonGreedyDecision || (ok && ds.getNonGreedy())
|
||||||
|
}
|
||||||
441
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/atn_config_set.go
generated
vendored
Normal file
441
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/atn_config_set.go
generated
vendored
Normal file
@@ -0,0 +1,441 @@
|
|||||||
|
// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
||||||
|
// Use of this file is governed by the BSD 3-clause license that
|
||||||
|
// can be found in the LICENSE.txt file in the project root.
|
||||||
|
|
||||||
|
package antlr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ATNConfigSet interface {
|
||||||
|
Hash() int
|
||||||
|
Equals(o Collectable[ATNConfig]) bool
|
||||||
|
Add(ATNConfig, *DoubleDict) bool
|
||||||
|
AddAll([]ATNConfig) bool
|
||||||
|
|
||||||
|
GetStates() *JStore[ATNState, Comparator[ATNState]]
|
||||||
|
GetPredicates() []SemanticContext
|
||||||
|
GetItems() []ATNConfig
|
||||||
|
|
||||||
|
OptimizeConfigs(interpreter *BaseATNSimulator)
|
||||||
|
|
||||||
|
Length() int
|
||||||
|
IsEmpty() bool
|
||||||
|
Contains(ATNConfig) bool
|
||||||
|
ContainsFast(ATNConfig) bool
|
||||||
|
Clear()
|
||||||
|
String() string
|
||||||
|
|
||||||
|
HasSemanticContext() bool
|
||||||
|
SetHasSemanticContext(v bool)
|
||||||
|
|
||||||
|
ReadOnly() bool
|
||||||
|
SetReadOnly(bool)
|
||||||
|
|
||||||
|
GetConflictingAlts() *BitSet
|
||||||
|
SetConflictingAlts(*BitSet)
|
||||||
|
|
||||||
|
Alts() *BitSet
|
||||||
|
|
||||||
|
FullContext() bool
|
||||||
|
|
||||||
|
GetUniqueAlt() int
|
||||||
|
SetUniqueAlt(int)
|
||||||
|
|
||||||
|
GetDipsIntoOuterContext() bool
|
||||||
|
SetDipsIntoOuterContext(bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BaseATNConfigSet is a specialized set of ATNConfig that tracks information
|
||||||
|
// about its elements and can combine similar configurations using a
|
||||||
|
// graph-structured stack.
|
||||||
|
type BaseATNConfigSet struct {
|
||||||
|
cachedHash int
|
||||||
|
|
||||||
|
// configLookup is used to determine whether two BaseATNConfigSets are equal. We
|
||||||
|
// need all configurations with the same (s, i, _, semctx) to be equal. A key
|
||||||
|
// effectively doubles the number of objects associated with ATNConfigs. All
|
||||||
|
// keys are hashed by (s, i, _, pi), not including the context. Wiped out when
|
||||||
|
// read-only because a set becomes a DFA state.
|
||||||
|
configLookup *JStore[ATNConfig, Comparator[ATNConfig]]
|
||||||
|
|
||||||
|
// configs is the added elements.
|
||||||
|
configs []ATNConfig
|
||||||
|
|
||||||
|
// TODO: These fields make me pretty uncomfortable, but it is nice to pack up
|
||||||
|
// info together because it saves recomputation. Can we track conflicts as they
|
||||||
|
// are added to save scanning configs later?
|
||||||
|
conflictingAlts *BitSet
|
||||||
|
|
||||||
|
// dipsIntoOuterContext is used by parsers and lexers. In a lexer, it indicates
|
||||||
|
// we hit a pred while computing a closure operation. Do not make a DFA state
|
||||||
|
// from the BaseATNConfigSet in this case. TODO: How is this used by parsers?
|
||||||
|
dipsIntoOuterContext bool
|
||||||
|
|
||||||
|
// fullCtx is whether it is part of a full context LL prediction. Used to
|
||||||
|
// determine how to merge $. It is a wildcard with SLL, but not for an LL
|
||||||
|
// context merge.
|
||||||
|
fullCtx bool
|
||||||
|
|
||||||
|
// Used in parser and lexer. In lexer, it indicates we hit a pred
|
||||||
|
// while computing a closure operation. Don't make a DFA state from a.
|
||||||
|
hasSemanticContext bool
|
||||||
|
|
||||||
|
// readOnly is whether it is read-only. Do not
|
||||||
|
// allow any code to manipulate the set if true because DFA states will point at
|
||||||
|
// sets and those must not change. It not, protect other fields; conflictingAlts
|
||||||
|
// in particular, which is assigned after readOnly.
|
||||||
|
readOnly bool
|
||||||
|
|
||||||
|
// TODO: These fields make me pretty uncomfortable, but it is nice to pack up
|
||||||
|
// info together because it saves recomputation. Can we track conflicts as they
|
||||||
|
// are added to save scanning configs later?
|
||||||
|
uniqueAlt int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseATNConfigSet) Alts() *BitSet {
|
||||||
|
alts := NewBitSet()
|
||||||
|
for _, it := range b.configs {
|
||||||
|
alts.add(it.GetAlt())
|
||||||
|
}
|
||||||
|
return alts
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBaseATNConfigSet(fullCtx bool) *BaseATNConfigSet {
|
||||||
|
return &BaseATNConfigSet{
|
||||||
|
cachedHash: -1,
|
||||||
|
configLookup: NewJStore[ATNConfig, Comparator[ATNConfig]](aConfCompInst),
|
||||||
|
fullCtx: fullCtx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add merges contexts with existing configs for (s, i, pi, _), where s is the
|
||||||
|
// ATNConfig.state, i is the ATNConfig.alt, and pi is the
|
||||||
|
// ATNConfig.semanticContext. We use (s,i,pi) as the key. Updates
|
||||||
|
// dipsIntoOuterContext and hasSemanticContext when necessary.
|
||||||
|
func (b *BaseATNConfigSet) Add(config ATNConfig, mergeCache *DoubleDict) bool {
|
||||||
|
if b.readOnly {
|
||||||
|
panic("set is read-only")
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.GetSemanticContext() != SemanticContextNone {
|
||||||
|
b.hasSemanticContext = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.GetReachesIntoOuterContext() > 0 {
|
||||||
|
b.dipsIntoOuterContext = true
|
||||||
|
}
|
||||||
|
|
||||||
|
existing, present := b.configLookup.Put(config)
|
||||||
|
|
||||||
|
// The config was not already in the set
|
||||||
|
//
|
||||||
|
if !present {
|
||||||
|
b.cachedHash = -1
|
||||||
|
b.configs = append(b.configs, config) // Track order here
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge a previous (s, i, pi, _) with it and save the result
|
||||||
|
rootIsWildcard := !b.fullCtx
|
||||||
|
merged := merge(existing.GetContext(), config.GetContext(), rootIsWildcard, mergeCache)
|
||||||
|
|
||||||
|
// No need to check for existing.context because config.context is in the cache,
|
||||||
|
// since the only way to create new graphs is the "call rule" and here. We cache
|
||||||
|
// at both places.
|
||||||
|
existing.SetReachesIntoOuterContext(intMax(existing.GetReachesIntoOuterContext(), config.GetReachesIntoOuterContext()))
|
||||||
|
|
||||||
|
// Preserve the precedence filter suppression during the merge
|
||||||
|
if config.getPrecedenceFilterSuppressed() {
|
||||||
|
existing.setPrecedenceFilterSuppressed(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace the context because there is no need to do alt mapping
|
||||||
|
existing.SetContext(merged)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseATNConfigSet) GetStates() *JStore[ATNState, Comparator[ATNState]] {
|
||||||
|
|
||||||
|
// states uses the standard comparator provided by the ATNState instance
|
||||||
|
//
|
||||||
|
states := NewJStore[ATNState, Comparator[ATNState]](aStateEqInst)
|
||||||
|
|
||||||
|
for i := 0; i < len(b.configs); i++ {
|
||||||
|
states.Put(b.configs[i].GetState())
|
||||||
|
}
|
||||||
|
|
||||||
|
return states
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseATNConfigSet) HasSemanticContext() bool {
|
||||||
|
return b.hasSemanticContext
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseATNConfigSet) SetHasSemanticContext(v bool) {
|
||||||
|
b.hasSemanticContext = v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseATNConfigSet) GetPredicates() []SemanticContext {
|
||||||
|
preds := make([]SemanticContext, 0)
|
||||||
|
|
||||||
|
for i := 0; i < len(b.configs); i++ {
|
||||||
|
c := b.configs[i].GetSemanticContext()
|
||||||
|
|
||||||
|
if c != SemanticContextNone {
|
||||||
|
preds = append(preds, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return preds
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseATNConfigSet) GetItems() []ATNConfig {
|
||||||
|
return b.configs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseATNConfigSet) OptimizeConfigs(interpreter *BaseATNSimulator) {
|
||||||
|
if b.readOnly {
|
||||||
|
panic("set is read-only")
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.configLookup.Len() == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(b.configs); i++ {
|
||||||
|
config := b.configs[i]
|
||||||
|
|
||||||
|
config.SetContext(interpreter.getCachedContext(config.GetContext()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseATNConfigSet) AddAll(coll []ATNConfig) bool {
|
||||||
|
for i := 0; i < len(coll); i++ {
|
||||||
|
b.Add(coll[i], nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare is a hack function just to verify that adding DFAstares to the known
|
||||||
|
// set works, so long as comparison of ATNConfigSet s works. For that to work, we
|
||||||
|
// need to make sure that the set of ATNConfigs in two sets are equivalent. We can't
|
||||||
|
// know the order, so we do this inefficient hack. If this proves the point, then
|
||||||
|
// we can change the config set to a better structure.
|
||||||
|
func (b *BaseATNConfigSet) Compare(bs *BaseATNConfigSet) bool {
|
||||||
|
if len(b.configs) != len(bs.configs) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range b.configs {
|
||||||
|
found := false
|
||||||
|
for _, c2 := range bs.configs {
|
||||||
|
if c.Equals(c2) {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseATNConfigSet) Equals(other Collectable[ATNConfig]) bool {
|
||||||
|
if b == other {
|
||||||
|
return true
|
||||||
|
} else if _, ok := other.(*BaseATNConfigSet); !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
other2 := other.(*BaseATNConfigSet)
|
||||||
|
|
||||||
|
return b.configs != nil &&
|
||||||
|
b.fullCtx == other2.fullCtx &&
|
||||||
|
b.uniqueAlt == other2.uniqueAlt &&
|
||||||
|
b.conflictingAlts == other2.conflictingAlts &&
|
||||||
|
b.hasSemanticContext == other2.hasSemanticContext &&
|
||||||
|
b.dipsIntoOuterContext == other2.dipsIntoOuterContext &&
|
||||||
|
b.Compare(other2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseATNConfigSet) Hash() int {
|
||||||
|
if b.readOnly {
|
||||||
|
if b.cachedHash == -1 {
|
||||||
|
b.cachedHash = b.hashCodeConfigs()
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.cachedHash
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.hashCodeConfigs()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseATNConfigSet) hashCodeConfigs() int {
|
||||||
|
h := 1
|
||||||
|
for _, config := range b.configs {
|
||||||
|
h = 31*h + config.Hash()
|
||||||
|
}
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseATNConfigSet) Length() int {
|
||||||
|
return len(b.configs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseATNConfigSet) IsEmpty() bool {
|
||||||
|
return len(b.configs) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseATNConfigSet) Contains(item ATNConfig) bool {
|
||||||
|
if b.configLookup == nil {
|
||||||
|
panic("not implemented for read-only sets")
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.configLookup.Contains(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseATNConfigSet) ContainsFast(item ATNConfig) bool {
|
||||||
|
if b.configLookup == nil {
|
||||||
|
panic("not implemented for read-only sets")
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.configLookup.Contains(item) // TODO: containsFast is not implemented for Set
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseATNConfigSet) Clear() {
|
||||||
|
if b.readOnly {
|
||||||
|
panic("set is read-only")
|
||||||
|
}
|
||||||
|
|
||||||
|
b.configs = make([]ATNConfig, 0)
|
||||||
|
b.cachedHash = -1
|
||||||
|
b.configLookup = NewJStore[ATNConfig, Comparator[ATNConfig]](atnConfCompInst)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseATNConfigSet) FullContext() bool {
|
||||||
|
return b.fullCtx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseATNConfigSet) GetDipsIntoOuterContext() bool {
|
||||||
|
return b.dipsIntoOuterContext
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseATNConfigSet) SetDipsIntoOuterContext(v bool) {
|
||||||
|
b.dipsIntoOuterContext = v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseATNConfigSet) GetUniqueAlt() int {
|
||||||
|
return b.uniqueAlt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseATNConfigSet) SetUniqueAlt(v int) {
|
||||||
|
b.uniqueAlt = v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseATNConfigSet) GetConflictingAlts() *BitSet {
|
||||||
|
return b.conflictingAlts
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseATNConfigSet) SetConflictingAlts(v *BitSet) {
|
||||||
|
b.conflictingAlts = v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseATNConfigSet) ReadOnly() bool {
|
||||||
|
return b.readOnly
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseATNConfigSet) SetReadOnly(readOnly bool) {
|
||||||
|
b.readOnly = readOnly
|
||||||
|
|
||||||
|
if readOnly {
|
||||||
|
b.configLookup = nil // Read only, so no need for the lookup cache
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseATNConfigSet) String() string {
|
||||||
|
s := "["
|
||||||
|
|
||||||
|
for i, c := range b.configs {
|
||||||
|
s += c.String()
|
||||||
|
|
||||||
|
if i != len(b.configs)-1 {
|
||||||
|
s += ", "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s += "]"
|
||||||
|
|
||||||
|
if b.hasSemanticContext {
|
||||||
|
s += ",hasSemanticContext=" + fmt.Sprint(b.hasSemanticContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.uniqueAlt != ATNInvalidAltNumber {
|
||||||
|
s += ",uniqueAlt=" + fmt.Sprint(b.uniqueAlt)
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.conflictingAlts != nil {
|
||||||
|
s += ",conflictingAlts=" + b.conflictingAlts.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.dipsIntoOuterContext {
|
||||||
|
s += ",dipsIntoOuterContext"
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
type OrderedATNConfigSet struct {
|
||||||
|
*BaseATNConfigSet
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewOrderedATNConfigSet() *OrderedATNConfigSet {
|
||||||
|
b := NewBaseATNConfigSet(false)
|
||||||
|
|
||||||
|
// This set uses the standard Hash() and Equals() from ATNConfig
|
||||||
|
b.configLookup = NewJStore[ATNConfig, Comparator[ATNConfig]](aConfEqInst)
|
||||||
|
|
||||||
|
return &OrderedATNConfigSet{BaseATNConfigSet: b}
|
||||||
|
}
|
||||||
|
|
||||||
|
func hashATNConfig(i interface{}) int {
|
||||||
|
o := i.(ATNConfig)
|
||||||
|
hash := 7
|
||||||
|
hash = 31*hash + o.GetState().GetStateNumber()
|
||||||
|
hash = 31*hash + o.GetAlt()
|
||||||
|
hash = 31*hash + o.GetSemanticContext().Hash()
|
||||||
|
return hash
|
||||||
|
}
|
||||||
|
|
||||||
|
func equalATNConfigs(a, b interface{}) bool {
|
||||||
|
if a == nil || b == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if a == b {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
var ai, ok = a.(ATNConfig)
|
||||||
|
var bi, ok1 = b.(ATNConfig)
|
||||||
|
|
||||||
|
if !ok || !ok1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if ai.GetState().GetStateNumber() != bi.GetState().GetStateNumber() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if ai.GetAlt() != bi.GetAlt() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return ai.GetSemanticContext().Equals(bi.GetSemanticContext())
|
||||||
|
}
|
||||||
61
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/atn_deserialization_options.go
generated
vendored
Normal file
61
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/atn_deserialization_options.go
generated
vendored
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
||||||
|
// Use of this file is governed by the BSD 3-clause license that
|
||||||
|
// can be found in the LICENSE.txt file in the project root.
|
||||||
|
|
||||||
|
package antlr
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
var defaultATNDeserializationOptions = ATNDeserializationOptions{true, true, false}
|
||||||
|
|
||||||
|
type ATNDeserializationOptions struct {
|
||||||
|
readOnly bool
|
||||||
|
verifyATN bool
|
||||||
|
generateRuleBypassTransitions bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opts *ATNDeserializationOptions) ReadOnly() bool {
|
||||||
|
return opts.readOnly
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opts *ATNDeserializationOptions) SetReadOnly(readOnly bool) {
|
||||||
|
if opts.readOnly {
|
||||||
|
panic(errors.New("Cannot mutate read only ATNDeserializationOptions"))
|
||||||
|
}
|
||||||
|
opts.readOnly = readOnly
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opts *ATNDeserializationOptions) VerifyATN() bool {
|
||||||
|
return opts.verifyATN
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opts *ATNDeserializationOptions) SetVerifyATN(verifyATN bool) {
|
||||||
|
if opts.readOnly {
|
||||||
|
panic(errors.New("Cannot mutate read only ATNDeserializationOptions"))
|
||||||
|
}
|
||||||
|
opts.verifyATN = verifyATN
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opts *ATNDeserializationOptions) GenerateRuleBypassTransitions() bool {
|
||||||
|
return opts.generateRuleBypassTransitions
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opts *ATNDeserializationOptions) SetGenerateRuleBypassTransitions(generateRuleBypassTransitions bool) {
|
||||||
|
if opts.readOnly {
|
||||||
|
panic(errors.New("Cannot mutate read only ATNDeserializationOptions"))
|
||||||
|
}
|
||||||
|
opts.generateRuleBypassTransitions = generateRuleBypassTransitions
|
||||||
|
}
|
||||||
|
|
||||||
|
func DefaultATNDeserializationOptions() *ATNDeserializationOptions {
|
||||||
|
return NewATNDeserializationOptions(&defaultATNDeserializationOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewATNDeserializationOptions(other *ATNDeserializationOptions) *ATNDeserializationOptions {
|
||||||
|
o := new(ATNDeserializationOptions)
|
||||||
|
if other != nil {
|
||||||
|
*o = *other
|
||||||
|
o.readOnly = false
|
||||||
|
}
|
||||||
|
return o
|
||||||
|
}
|
||||||
683
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/atn_deserializer.go
generated
vendored
Normal file
683
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/atn_deserializer.go
generated
vendored
Normal file
@@ -0,0 +1,683 @@
|
|||||||
|
// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
||||||
|
// Use of this file is governed by the BSD 3-clause license that
|
||||||
|
// can be found in the LICENSE.txt file in the project root.
|
||||||
|
|
||||||
|
package antlr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
const serializedVersion = 4
|
||||||
|
|
||||||
|
type loopEndStateIntPair struct {
|
||||||
|
item0 *LoopEndState
|
||||||
|
item1 int
|
||||||
|
}
|
||||||
|
|
||||||
|
type blockStartStateIntPair struct {
|
||||||
|
item0 BlockStartState
|
||||||
|
item1 int
|
||||||
|
}
|
||||||
|
|
||||||
|
type ATNDeserializer struct {
|
||||||
|
options *ATNDeserializationOptions
|
||||||
|
data []int32
|
||||||
|
pos int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewATNDeserializer(options *ATNDeserializationOptions) *ATNDeserializer {
|
||||||
|
if options == nil {
|
||||||
|
options = &defaultATNDeserializationOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ATNDeserializer{options: options}
|
||||||
|
}
|
||||||
|
|
||||||
|
func stringInSlice(a string, list []string) int {
|
||||||
|
for i, b := range list {
|
||||||
|
if b == a {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ATNDeserializer) Deserialize(data []int32) *ATN {
|
||||||
|
a.data = data
|
||||||
|
a.pos = 0
|
||||||
|
a.checkVersion()
|
||||||
|
|
||||||
|
atn := a.readATN()
|
||||||
|
|
||||||
|
a.readStates(atn)
|
||||||
|
a.readRules(atn)
|
||||||
|
a.readModes(atn)
|
||||||
|
|
||||||
|
sets := a.readSets(atn, nil)
|
||||||
|
|
||||||
|
a.readEdges(atn, sets)
|
||||||
|
a.readDecisions(atn)
|
||||||
|
a.readLexerActions(atn)
|
||||||
|
a.markPrecedenceDecisions(atn)
|
||||||
|
a.verifyATN(atn)
|
||||||
|
|
||||||
|
if a.options.GenerateRuleBypassTransitions() && atn.grammarType == ATNTypeParser {
|
||||||
|
a.generateRuleBypassTransitions(atn)
|
||||||
|
// Re-verify after modification
|
||||||
|
a.verifyATN(atn)
|
||||||
|
}
|
||||||
|
|
||||||
|
return atn
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ATNDeserializer) checkVersion() {
|
||||||
|
version := a.readInt()
|
||||||
|
|
||||||
|
if version != serializedVersion {
|
||||||
|
panic("Could not deserialize ATN with version " + strconv.Itoa(version) + " (expected " + strconv.Itoa(serializedVersion) + ").")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ATNDeserializer) readATN() *ATN {
|
||||||
|
grammarType := a.readInt()
|
||||||
|
maxTokenType := a.readInt()
|
||||||
|
|
||||||
|
return NewATN(grammarType, maxTokenType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ATNDeserializer) readStates(atn *ATN) {
|
||||||
|
nstates := a.readInt()
|
||||||
|
|
||||||
|
// Allocate worst case size.
|
||||||
|
loopBackStateNumbers := make([]loopEndStateIntPair, 0, nstates)
|
||||||
|
endStateNumbers := make([]blockStartStateIntPair, 0, nstates)
|
||||||
|
|
||||||
|
// Preallocate states slice.
|
||||||
|
atn.states = make([]ATNState, 0, nstates)
|
||||||
|
|
||||||
|
for i := 0; i < nstates; i++ {
|
||||||
|
stype := a.readInt()
|
||||||
|
|
||||||
|
// Ignore bad types of states
|
||||||
|
if stype == ATNStateInvalidType {
|
||||||
|
atn.addState(nil)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ruleIndex := a.readInt()
|
||||||
|
|
||||||
|
s := a.stateFactory(stype, ruleIndex)
|
||||||
|
|
||||||
|
if stype == ATNStateLoopEnd {
|
||||||
|
loopBackStateNumber := a.readInt()
|
||||||
|
|
||||||
|
loopBackStateNumbers = append(loopBackStateNumbers, loopEndStateIntPair{s.(*LoopEndState), loopBackStateNumber})
|
||||||
|
} else if s2, ok := s.(BlockStartState); ok {
|
||||||
|
endStateNumber := a.readInt()
|
||||||
|
|
||||||
|
endStateNumbers = append(endStateNumbers, blockStartStateIntPair{s2, endStateNumber})
|
||||||
|
}
|
||||||
|
|
||||||
|
atn.addState(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delay the assignment of loop back and end states until we know all the state
|
||||||
|
// instances have been initialized
|
||||||
|
for _, pair := range loopBackStateNumbers {
|
||||||
|
pair.item0.loopBackState = atn.states[pair.item1]
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, pair := range endStateNumbers {
|
||||||
|
pair.item0.setEndState(atn.states[pair.item1].(*BlockEndState))
|
||||||
|
}
|
||||||
|
|
||||||
|
numNonGreedyStates := a.readInt()
|
||||||
|
for j := 0; j < numNonGreedyStates; j++ {
|
||||||
|
stateNumber := a.readInt()
|
||||||
|
|
||||||
|
atn.states[stateNumber].(DecisionState).setNonGreedy(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
numPrecedenceStates := a.readInt()
|
||||||
|
for j := 0; j < numPrecedenceStates; j++ {
|
||||||
|
stateNumber := a.readInt()
|
||||||
|
|
||||||
|
atn.states[stateNumber].(*RuleStartState).isPrecedenceRule = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ATNDeserializer) readRules(atn *ATN) {
|
||||||
|
nrules := a.readInt()
|
||||||
|
|
||||||
|
if atn.grammarType == ATNTypeLexer {
|
||||||
|
atn.ruleToTokenType = make([]int, nrules)
|
||||||
|
}
|
||||||
|
|
||||||
|
atn.ruleToStartState = make([]*RuleStartState, nrules)
|
||||||
|
|
||||||
|
for i := range atn.ruleToStartState {
|
||||||
|
s := a.readInt()
|
||||||
|
startState := atn.states[s].(*RuleStartState)
|
||||||
|
|
||||||
|
atn.ruleToStartState[i] = startState
|
||||||
|
|
||||||
|
if atn.grammarType == ATNTypeLexer {
|
||||||
|
tokenType := a.readInt()
|
||||||
|
|
||||||
|
atn.ruleToTokenType[i] = tokenType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
atn.ruleToStopState = make([]*RuleStopState, nrules)
|
||||||
|
|
||||||
|
for _, state := range atn.states {
|
||||||
|
if s2, ok := state.(*RuleStopState); ok {
|
||||||
|
atn.ruleToStopState[s2.ruleIndex] = s2
|
||||||
|
atn.ruleToStartState[s2.ruleIndex].stopState = s2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ATNDeserializer) readModes(atn *ATN) {
|
||||||
|
nmodes := a.readInt()
|
||||||
|
atn.modeToStartState = make([]*TokensStartState, nmodes)
|
||||||
|
|
||||||
|
for i := range atn.modeToStartState {
|
||||||
|
s := a.readInt()
|
||||||
|
|
||||||
|
atn.modeToStartState[i] = atn.states[s].(*TokensStartState)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ATNDeserializer) readSets(atn *ATN, sets []*IntervalSet) []*IntervalSet {
|
||||||
|
m := a.readInt()
|
||||||
|
|
||||||
|
// Preallocate the needed capacity.
|
||||||
|
if cap(sets)-len(sets) < m {
|
||||||
|
isets := make([]*IntervalSet, len(sets), len(sets)+m)
|
||||||
|
copy(isets, sets)
|
||||||
|
sets = isets
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < m; i++ {
|
||||||
|
iset := NewIntervalSet()
|
||||||
|
|
||||||
|
sets = append(sets, iset)
|
||||||
|
|
||||||
|
n := a.readInt()
|
||||||
|
containsEOF := a.readInt()
|
||||||
|
|
||||||
|
if containsEOF != 0 {
|
||||||
|
iset.addOne(-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
for j := 0; j < n; j++ {
|
||||||
|
i1 := a.readInt()
|
||||||
|
i2 := a.readInt()
|
||||||
|
|
||||||
|
iset.addRange(i1, i2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sets
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ATNDeserializer) readEdges(atn *ATN, sets []*IntervalSet) {
|
||||||
|
nedges := a.readInt()
|
||||||
|
|
||||||
|
for i := 0; i < nedges; i++ {
|
||||||
|
var (
|
||||||
|
src = a.readInt()
|
||||||
|
trg = a.readInt()
|
||||||
|
ttype = a.readInt()
|
||||||
|
arg1 = a.readInt()
|
||||||
|
arg2 = a.readInt()
|
||||||
|
arg3 = a.readInt()
|
||||||
|
trans = a.edgeFactory(atn, ttype, src, trg, arg1, arg2, arg3, sets)
|
||||||
|
srcState = atn.states[src]
|
||||||
|
)
|
||||||
|
|
||||||
|
srcState.AddTransition(trans, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Edges for rule stop states can be derived, so they are not serialized
|
||||||
|
for _, state := range atn.states {
|
||||||
|
for _, t := range state.GetTransitions() {
|
||||||
|
var rt, ok = t.(*RuleTransition)
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
outermostPrecedenceReturn := -1
|
||||||
|
|
||||||
|
if atn.ruleToStartState[rt.getTarget().GetRuleIndex()].isPrecedenceRule {
|
||||||
|
if rt.precedence == 0 {
|
||||||
|
outermostPrecedenceReturn = rt.getTarget().GetRuleIndex()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trans := NewEpsilonTransition(rt.followState, outermostPrecedenceReturn)
|
||||||
|
|
||||||
|
atn.ruleToStopState[rt.getTarget().GetRuleIndex()].AddTransition(trans, -1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, state := range atn.states {
|
||||||
|
if s2, ok := state.(BlockStartState); ok {
|
||||||
|
// We need to know the end state to set its start state
|
||||||
|
if s2.getEndState() == nil {
|
||||||
|
panic("IllegalState")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Block end states can only be associated to a single block start state
|
||||||
|
if s2.getEndState().startState != nil {
|
||||||
|
panic("IllegalState")
|
||||||
|
}
|
||||||
|
|
||||||
|
s2.getEndState().startState = state
|
||||||
|
}
|
||||||
|
|
||||||
|
if s2, ok := state.(*PlusLoopbackState); ok {
|
||||||
|
for _, t := range s2.GetTransitions() {
|
||||||
|
if t2, ok := t.getTarget().(*PlusBlockStartState); ok {
|
||||||
|
t2.loopBackState = state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if s2, ok := state.(*StarLoopbackState); ok {
|
||||||
|
for _, t := range s2.GetTransitions() {
|
||||||
|
if t2, ok := t.getTarget().(*StarLoopEntryState); ok {
|
||||||
|
t2.loopBackState = state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ATNDeserializer) readDecisions(atn *ATN) {
|
||||||
|
ndecisions := a.readInt()
|
||||||
|
|
||||||
|
for i := 0; i < ndecisions; i++ {
|
||||||
|
s := a.readInt()
|
||||||
|
decState := atn.states[s].(DecisionState)
|
||||||
|
|
||||||
|
atn.DecisionToState = append(atn.DecisionToState, decState)
|
||||||
|
decState.setDecision(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ATNDeserializer) readLexerActions(atn *ATN) {
|
||||||
|
if atn.grammarType == ATNTypeLexer {
|
||||||
|
count := a.readInt()
|
||||||
|
|
||||||
|
atn.lexerActions = make([]LexerAction, count)
|
||||||
|
|
||||||
|
for i := range atn.lexerActions {
|
||||||
|
actionType := a.readInt()
|
||||||
|
data1 := a.readInt()
|
||||||
|
data2 := a.readInt()
|
||||||
|
atn.lexerActions[i] = a.lexerActionFactory(actionType, data1, data2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ATNDeserializer) generateRuleBypassTransitions(atn *ATN) {
|
||||||
|
count := len(atn.ruleToStartState)
|
||||||
|
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
atn.ruleToTokenType[i] = atn.maxTokenType + i + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < count; i++ {
|
||||||
|
a.generateRuleBypassTransition(atn, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ATNDeserializer) generateRuleBypassTransition(atn *ATN, idx int) {
|
||||||
|
bypassStart := NewBasicBlockStartState()
|
||||||
|
|
||||||
|
bypassStart.ruleIndex = idx
|
||||||
|
atn.addState(bypassStart)
|
||||||
|
|
||||||
|
bypassStop := NewBlockEndState()
|
||||||
|
|
||||||
|
bypassStop.ruleIndex = idx
|
||||||
|
atn.addState(bypassStop)
|
||||||
|
|
||||||
|
bypassStart.endState = bypassStop
|
||||||
|
|
||||||
|
atn.defineDecisionState(bypassStart.BaseDecisionState)
|
||||||
|
|
||||||
|
bypassStop.startState = bypassStart
|
||||||
|
|
||||||
|
var excludeTransition Transition
|
||||||
|
var endState ATNState
|
||||||
|
|
||||||
|
if atn.ruleToStartState[idx].isPrecedenceRule {
|
||||||
|
// Wrap from the beginning of the rule to the StarLoopEntryState
|
||||||
|
endState = nil
|
||||||
|
|
||||||
|
for i := 0; i < len(atn.states); i++ {
|
||||||
|
state := atn.states[i]
|
||||||
|
|
||||||
|
if a.stateIsEndStateFor(state, idx) != nil {
|
||||||
|
endState = state
|
||||||
|
excludeTransition = state.(*StarLoopEntryState).loopBackState.GetTransitions()[0]
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if excludeTransition == nil {
|
||||||
|
panic("Couldn't identify final state of the precedence rule prefix section.")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
endState = atn.ruleToStopState[idx]
|
||||||
|
}
|
||||||
|
|
||||||
|
// All non-excluded transitions that currently target end state need to target
|
||||||
|
// blockEnd instead
|
||||||
|
for i := 0; i < len(atn.states); i++ {
|
||||||
|
state := atn.states[i]
|
||||||
|
|
||||||
|
for j := 0; j < len(state.GetTransitions()); j++ {
|
||||||
|
transition := state.GetTransitions()[j]
|
||||||
|
|
||||||
|
if transition == excludeTransition {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if transition.getTarget() == endState {
|
||||||
|
transition.setTarget(bypassStop)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// All transitions leaving the rule start state need to leave blockStart instead
|
||||||
|
ruleToStartState := atn.ruleToStartState[idx]
|
||||||
|
count := len(ruleToStartState.GetTransitions())
|
||||||
|
|
||||||
|
for count > 0 {
|
||||||
|
bypassStart.AddTransition(ruleToStartState.GetTransitions()[count-1], -1)
|
||||||
|
ruleToStartState.SetTransitions([]Transition{ruleToStartState.GetTransitions()[len(ruleToStartState.GetTransitions())-1]})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Link the new states
|
||||||
|
atn.ruleToStartState[idx].AddTransition(NewEpsilonTransition(bypassStart, -1), -1)
|
||||||
|
bypassStop.AddTransition(NewEpsilonTransition(endState, -1), -1)
|
||||||
|
|
||||||
|
MatchState := NewBasicState()
|
||||||
|
|
||||||
|
atn.addState(MatchState)
|
||||||
|
MatchState.AddTransition(NewAtomTransition(bypassStop, atn.ruleToTokenType[idx]), -1)
|
||||||
|
bypassStart.AddTransition(NewEpsilonTransition(MatchState, -1), -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ATNDeserializer) stateIsEndStateFor(state ATNState, idx int) ATNState {
|
||||||
|
if state.GetRuleIndex() != idx {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := state.(*StarLoopEntryState); !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
maybeLoopEndState := state.GetTransitions()[len(state.GetTransitions())-1].getTarget()
|
||||||
|
|
||||||
|
if _, ok := maybeLoopEndState.(*LoopEndState); !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _, ok = maybeLoopEndState.GetTransitions()[0].getTarget().(*RuleStopState)
|
||||||
|
|
||||||
|
if maybeLoopEndState.(*LoopEndState).epsilonOnlyTransitions && ok {
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// markPrecedenceDecisions analyzes the StarLoopEntryState states in the
|
||||||
|
// specified ATN to set the StarLoopEntryState.precedenceRuleDecision field to
|
||||||
|
// the correct value.
|
||||||
|
func (a *ATNDeserializer) markPrecedenceDecisions(atn *ATN) {
|
||||||
|
for _, state := range atn.states {
|
||||||
|
if _, ok := state.(*StarLoopEntryState); !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// We analyze the ATN to determine if a ATN decision state is the
|
||||||
|
// decision for the closure block that determines whether a
|
||||||
|
// precedence rule should continue or complete.
|
||||||
|
if atn.ruleToStartState[state.GetRuleIndex()].isPrecedenceRule {
|
||||||
|
maybeLoopEndState := state.GetTransitions()[len(state.GetTransitions())-1].getTarget()
|
||||||
|
|
||||||
|
if s3, ok := maybeLoopEndState.(*LoopEndState); ok {
|
||||||
|
var _, ok2 = maybeLoopEndState.GetTransitions()[0].getTarget().(*RuleStopState)
|
||||||
|
|
||||||
|
if s3.epsilonOnlyTransitions && ok2 {
|
||||||
|
state.(*StarLoopEntryState).precedenceRuleDecision = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ATNDeserializer) verifyATN(atn *ATN) {
|
||||||
|
if !a.options.VerifyATN() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify assumptions
|
||||||
|
for _, state := range atn.states {
|
||||||
|
if state == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
a.checkCondition(state.GetEpsilonOnlyTransitions() || len(state.GetTransitions()) <= 1, "")
|
||||||
|
|
||||||
|
switch s2 := state.(type) {
|
||||||
|
case *PlusBlockStartState:
|
||||||
|
a.checkCondition(s2.loopBackState != nil, "")
|
||||||
|
|
||||||
|
case *StarLoopEntryState:
|
||||||
|
a.checkCondition(s2.loopBackState != nil, "")
|
||||||
|
a.checkCondition(len(s2.GetTransitions()) == 2, "")
|
||||||
|
|
||||||
|
switch s2.transitions[0].getTarget().(type) {
|
||||||
|
case *StarBlockStartState:
|
||||||
|
_, ok := s2.transitions[1].getTarget().(*LoopEndState)
|
||||||
|
|
||||||
|
a.checkCondition(ok, "")
|
||||||
|
a.checkCondition(!s2.nonGreedy, "")
|
||||||
|
|
||||||
|
case *LoopEndState:
|
||||||
|
var _, ok = s2.transitions[1].getTarget().(*StarBlockStartState)
|
||||||
|
|
||||||
|
a.checkCondition(ok, "")
|
||||||
|
a.checkCondition(s2.nonGreedy, "")
|
||||||
|
|
||||||
|
default:
|
||||||
|
panic("IllegalState")
|
||||||
|
}
|
||||||
|
|
||||||
|
case *StarLoopbackState:
|
||||||
|
a.checkCondition(len(state.GetTransitions()) == 1, "")
|
||||||
|
|
||||||
|
var _, ok = state.GetTransitions()[0].getTarget().(*StarLoopEntryState)
|
||||||
|
|
||||||
|
a.checkCondition(ok, "")
|
||||||
|
|
||||||
|
case *LoopEndState:
|
||||||
|
a.checkCondition(s2.loopBackState != nil, "")
|
||||||
|
|
||||||
|
case *RuleStartState:
|
||||||
|
a.checkCondition(s2.stopState != nil, "")
|
||||||
|
|
||||||
|
case BlockStartState:
|
||||||
|
a.checkCondition(s2.getEndState() != nil, "")
|
||||||
|
|
||||||
|
case *BlockEndState:
|
||||||
|
a.checkCondition(s2.startState != nil, "")
|
||||||
|
|
||||||
|
case DecisionState:
|
||||||
|
a.checkCondition(len(s2.GetTransitions()) <= 1 || s2.getDecision() >= 0, "")
|
||||||
|
|
||||||
|
default:
|
||||||
|
var _, ok = s2.(*RuleStopState)
|
||||||
|
|
||||||
|
a.checkCondition(len(s2.GetTransitions()) <= 1 || ok, "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ATNDeserializer) checkCondition(condition bool, message string) {
|
||||||
|
if !condition {
|
||||||
|
if message == "" {
|
||||||
|
message = "IllegalState"
|
||||||
|
}
|
||||||
|
|
||||||
|
panic(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ATNDeserializer) readInt() int {
|
||||||
|
v := a.data[a.pos]
|
||||||
|
|
||||||
|
a.pos++
|
||||||
|
|
||||||
|
return int(v) // data is 32 bits but int is at least that big
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ATNDeserializer) edgeFactory(atn *ATN, typeIndex, src, trg, arg1, arg2, arg3 int, sets []*IntervalSet) Transition {
|
||||||
|
target := atn.states[trg]
|
||||||
|
|
||||||
|
switch typeIndex {
|
||||||
|
case TransitionEPSILON:
|
||||||
|
return NewEpsilonTransition(target, -1)
|
||||||
|
|
||||||
|
case TransitionRANGE:
|
||||||
|
if arg3 != 0 {
|
||||||
|
return NewRangeTransition(target, TokenEOF, arg2)
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewRangeTransition(target, arg1, arg2)
|
||||||
|
|
||||||
|
case TransitionRULE:
|
||||||
|
return NewRuleTransition(atn.states[arg1], arg2, arg3, target)
|
||||||
|
|
||||||
|
case TransitionPREDICATE:
|
||||||
|
return NewPredicateTransition(target, arg1, arg2, arg3 != 0)
|
||||||
|
|
||||||
|
case TransitionPRECEDENCE:
|
||||||
|
return NewPrecedencePredicateTransition(target, arg1)
|
||||||
|
|
||||||
|
case TransitionATOM:
|
||||||
|
if arg3 != 0 {
|
||||||
|
return NewAtomTransition(target, TokenEOF)
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewAtomTransition(target, arg1)
|
||||||
|
|
||||||
|
case TransitionACTION:
|
||||||
|
return NewActionTransition(target, arg1, arg2, arg3 != 0)
|
||||||
|
|
||||||
|
case TransitionSET:
|
||||||
|
return NewSetTransition(target, sets[arg1])
|
||||||
|
|
||||||
|
case TransitionNOTSET:
|
||||||
|
return NewNotSetTransition(target, sets[arg1])
|
||||||
|
|
||||||
|
case TransitionWILDCARD:
|
||||||
|
return NewWildcardTransition(target)
|
||||||
|
}
|
||||||
|
|
||||||
|
panic("The specified transition type is not valid.")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ATNDeserializer) stateFactory(typeIndex, ruleIndex int) ATNState {
|
||||||
|
var s ATNState
|
||||||
|
|
||||||
|
switch typeIndex {
|
||||||
|
case ATNStateInvalidType:
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case ATNStateBasic:
|
||||||
|
s = NewBasicState()
|
||||||
|
|
||||||
|
case ATNStateRuleStart:
|
||||||
|
s = NewRuleStartState()
|
||||||
|
|
||||||
|
case ATNStateBlockStart:
|
||||||
|
s = NewBasicBlockStartState()
|
||||||
|
|
||||||
|
case ATNStatePlusBlockStart:
|
||||||
|
s = NewPlusBlockStartState()
|
||||||
|
|
||||||
|
case ATNStateStarBlockStart:
|
||||||
|
s = NewStarBlockStartState()
|
||||||
|
|
||||||
|
case ATNStateTokenStart:
|
||||||
|
s = NewTokensStartState()
|
||||||
|
|
||||||
|
case ATNStateRuleStop:
|
||||||
|
s = NewRuleStopState()
|
||||||
|
|
||||||
|
case ATNStateBlockEnd:
|
||||||
|
s = NewBlockEndState()
|
||||||
|
|
||||||
|
case ATNStateStarLoopBack:
|
||||||
|
s = NewStarLoopbackState()
|
||||||
|
|
||||||
|
case ATNStateStarLoopEntry:
|
||||||
|
s = NewStarLoopEntryState()
|
||||||
|
|
||||||
|
case ATNStatePlusLoopBack:
|
||||||
|
s = NewPlusLoopbackState()
|
||||||
|
|
||||||
|
case ATNStateLoopEnd:
|
||||||
|
s = NewLoopEndState()
|
||||||
|
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("state type %d is invalid", typeIndex))
|
||||||
|
}
|
||||||
|
|
||||||
|
s.SetRuleIndex(ruleIndex)
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ATNDeserializer) lexerActionFactory(typeIndex, data1, data2 int) LexerAction {
|
||||||
|
switch typeIndex {
|
||||||
|
case LexerActionTypeChannel:
|
||||||
|
return NewLexerChannelAction(data1)
|
||||||
|
|
||||||
|
case LexerActionTypeCustom:
|
||||||
|
return NewLexerCustomAction(data1, data2)
|
||||||
|
|
||||||
|
case LexerActionTypeMode:
|
||||||
|
return NewLexerModeAction(data1)
|
||||||
|
|
||||||
|
case LexerActionTypeMore:
|
||||||
|
return LexerMoreActionINSTANCE
|
||||||
|
|
||||||
|
case LexerActionTypePopMode:
|
||||||
|
return LexerPopModeActionINSTANCE
|
||||||
|
|
||||||
|
case LexerActionTypePushMode:
|
||||||
|
return NewLexerPushModeAction(data1)
|
||||||
|
|
||||||
|
case LexerActionTypeSkip:
|
||||||
|
return LexerSkipActionINSTANCE
|
||||||
|
|
||||||
|
case LexerActionTypeType:
|
||||||
|
return NewLexerTypeAction(data1)
|
||||||
|
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("lexer action %d is invalid", typeIndex))
|
||||||
|
}
|
||||||
|
}
|
||||||
50
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/atn_simulator.go
generated
vendored
Normal file
50
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/atn_simulator.go
generated
vendored
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
||||||
|
// Use of this file is governed by the BSD 3-clause license that
|
||||||
|
// can be found in the LICENSE.txt file in the project root.
|
||||||
|
|
||||||
|
package antlr
|
||||||
|
|
||||||
|
var ATNSimulatorError = NewDFAState(0x7FFFFFFF, NewBaseATNConfigSet(false))
|
||||||
|
|
||||||
|
type IATNSimulator interface {
|
||||||
|
SharedContextCache() *PredictionContextCache
|
||||||
|
ATN() *ATN
|
||||||
|
DecisionToDFA() []*DFA
|
||||||
|
}
|
||||||
|
|
||||||
|
type BaseATNSimulator struct {
|
||||||
|
atn *ATN
|
||||||
|
sharedContextCache *PredictionContextCache
|
||||||
|
decisionToDFA []*DFA
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBaseATNSimulator(atn *ATN, sharedContextCache *PredictionContextCache) *BaseATNSimulator {
|
||||||
|
b := new(BaseATNSimulator)
|
||||||
|
|
||||||
|
b.atn = atn
|
||||||
|
b.sharedContextCache = sharedContextCache
|
||||||
|
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseATNSimulator) getCachedContext(context PredictionContext) PredictionContext {
|
||||||
|
if b.sharedContextCache == nil {
|
||||||
|
return context
|
||||||
|
}
|
||||||
|
|
||||||
|
visited := make(map[PredictionContext]PredictionContext)
|
||||||
|
|
||||||
|
return getCachedBasePredictionContext(context, b.sharedContextCache, visited)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseATNSimulator) SharedContextCache() *PredictionContextCache {
|
||||||
|
return b.sharedContextCache
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseATNSimulator) ATN() *ATN {
|
||||||
|
return b.atn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseATNSimulator) DecisionToDFA() []*DFA {
|
||||||
|
return b.decisionToDFA
|
||||||
|
}
|
||||||
393
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/atn_state.go
generated
vendored
Normal file
393
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/atn_state.go
generated
vendored
Normal file
@@ -0,0 +1,393 @@
|
|||||||
|
// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
||||||
|
// Use of this file is governed by the BSD 3-clause license that
|
||||||
|
// can be found in the LICENSE.txt file in the project root.
|
||||||
|
|
||||||
|
package antlr
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
// Constants for serialization.
|
||||||
|
const (
|
||||||
|
ATNStateInvalidType = 0
|
||||||
|
ATNStateBasic = 1
|
||||||
|
ATNStateRuleStart = 2
|
||||||
|
ATNStateBlockStart = 3
|
||||||
|
ATNStatePlusBlockStart = 4
|
||||||
|
ATNStateStarBlockStart = 5
|
||||||
|
ATNStateTokenStart = 6
|
||||||
|
ATNStateRuleStop = 7
|
||||||
|
ATNStateBlockEnd = 8
|
||||||
|
ATNStateStarLoopBack = 9
|
||||||
|
ATNStateStarLoopEntry = 10
|
||||||
|
ATNStatePlusLoopBack = 11
|
||||||
|
ATNStateLoopEnd = 12
|
||||||
|
|
||||||
|
ATNStateInvalidStateNumber = -1
|
||||||
|
)
|
||||||
|
|
||||||
|
var ATNStateInitialNumTransitions = 4
|
||||||
|
|
||||||
|
type ATNState interface {
|
||||||
|
GetEpsilonOnlyTransitions() bool
|
||||||
|
|
||||||
|
GetRuleIndex() int
|
||||||
|
SetRuleIndex(int)
|
||||||
|
|
||||||
|
GetNextTokenWithinRule() *IntervalSet
|
||||||
|
SetNextTokenWithinRule(*IntervalSet)
|
||||||
|
|
||||||
|
GetATN() *ATN
|
||||||
|
SetATN(*ATN)
|
||||||
|
|
||||||
|
GetStateType() int
|
||||||
|
|
||||||
|
GetStateNumber() int
|
||||||
|
SetStateNumber(int)
|
||||||
|
|
||||||
|
GetTransitions() []Transition
|
||||||
|
SetTransitions([]Transition)
|
||||||
|
AddTransition(Transition, int)
|
||||||
|
|
||||||
|
String() string
|
||||||
|
Hash() int
|
||||||
|
Equals(Collectable[ATNState]) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type BaseATNState struct {
|
||||||
|
// NextTokenWithinRule caches lookahead during parsing. Not used during construction.
|
||||||
|
NextTokenWithinRule *IntervalSet
|
||||||
|
|
||||||
|
// atn is the current ATN.
|
||||||
|
atn *ATN
|
||||||
|
|
||||||
|
epsilonOnlyTransitions bool
|
||||||
|
|
||||||
|
// ruleIndex tracks the Rule index because there are no Rule objects at runtime.
|
||||||
|
ruleIndex int
|
||||||
|
|
||||||
|
stateNumber int
|
||||||
|
|
||||||
|
stateType int
|
||||||
|
|
||||||
|
// Track the transitions emanating from this ATN state.
|
||||||
|
transitions []Transition
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBaseATNState() *BaseATNState {
|
||||||
|
return &BaseATNState{stateNumber: ATNStateInvalidStateNumber, stateType: ATNStateInvalidType}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as *BaseATNState) GetRuleIndex() int {
|
||||||
|
return as.ruleIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as *BaseATNState) SetRuleIndex(v int) {
|
||||||
|
as.ruleIndex = v
|
||||||
|
}
|
||||||
|
func (as *BaseATNState) GetEpsilonOnlyTransitions() bool {
|
||||||
|
return as.epsilonOnlyTransitions
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as *BaseATNState) GetATN() *ATN {
|
||||||
|
return as.atn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as *BaseATNState) SetATN(atn *ATN) {
|
||||||
|
as.atn = atn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as *BaseATNState) GetTransitions() []Transition {
|
||||||
|
return as.transitions
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as *BaseATNState) SetTransitions(t []Transition) {
|
||||||
|
as.transitions = t
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as *BaseATNState) GetStateType() int {
|
||||||
|
return as.stateType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as *BaseATNState) GetStateNumber() int {
|
||||||
|
return as.stateNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as *BaseATNState) SetStateNumber(stateNumber int) {
|
||||||
|
as.stateNumber = stateNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as *BaseATNState) GetNextTokenWithinRule() *IntervalSet {
|
||||||
|
return as.NextTokenWithinRule
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as *BaseATNState) SetNextTokenWithinRule(v *IntervalSet) {
|
||||||
|
as.NextTokenWithinRule = v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as *BaseATNState) Hash() int {
|
||||||
|
return as.stateNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as *BaseATNState) String() string {
|
||||||
|
return strconv.Itoa(as.stateNumber)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as *BaseATNState) Equals(other Collectable[ATNState]) bool {
|
||||||
|
if ot, ok := other.(ATNState); ok {
|
||||||
|
return as.stateNumber == ot.GetStateNumber()
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as *BaseATNState) isNonGreedyExitState() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as *BaseATNState) AddTransition(trans Transition, index int) {
|
||||||
|
if len(as.transitions) == 0 {
|
||||||
|
as.epsilonOnlyTransitions = trans.getIsEpsilon()
|
||||||
|
} else if as.epsilonOnlyTransitions != trans.getIsEpsilon() {
|
||||||
|
as.epsilonOnlyTransitions = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if index == -1 {
|
||||||
|
as.transitions = append(as.transitions, trans)
|
||||||
|
} else {
|
||||||
|
as.transitions = append(as.transitions[:index], append([]Transition{trans}, as.transitions[index:]...)...)
|
||||||
|
// TODO: as.transitions.splice(index, 1, trans)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type BasicState struct {
|
||||||
|
*BaseATNState
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBasicState() *BasicState {
|
||||||
|
b := NewBaseATNState()
|
||||||
|
|
||||||
|
b.stateType = ATNStateBasic
|
||||||
|
|
||||||
|
return &BasicState{BaseATNState: b}
|
||||||
|
}
|
||||||
|
|
||||||
|
type DecisionState interface {
|
||||||
|
ATNState
|
||||||
|
|
||||||
|
getDecision() int
|
||||||
|
setDecision(int)
|
||||||
|
|
||||||
|
getNonGreedy() bool
|
||||||
|
setNonGreedy(bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
type BaseDecisionState struct {
|
||||||
|
*BaseATNState
|
||||||
|
decision int
|
||||||
|
nonGreedy bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBaseDecisionState() *BaseDecisionState {
|
||||||
|
return &BaseDecisionState{BaseATNState: NewBaseATNState(), decision: -1}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BaseDecisionState) getDecision() int {
|
||||||
|
return s.decision
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BaseDecisionState) setDecision(b int) {
|
||||||
|
s.decision = b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BaseDecisionState) getNonGreedy() bool {
|
||||||
|
return s.nonGreedy
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BaseDecisionState) setNonGreedy(b bool) {
|
||||||
|
s.nonGreedy = b
|
||||||
|
}
|
||||||
|
|
||||||
|
type BlockStartState interface {
|
||||||
|
DecisionState
|
||||||
|
|
||||||
|
getEndState() *BlockEndState
|
||||||
|
setEndState(*BlockEndState)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BaseBlockStartState is the start of a regular (...) block.
|
||||||
|
type BaseBlockStartState struct {
|
||||||
|
*BaseDecisionState
|
||||||
|
endState *BlockEndState
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBlockStartState() *BaseBlockStartState {
|
||||||
|
return &BaseBlockStartState{BaseDecisionState: NewBaseDecisionState()}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BaseBlockStartState) getEndState() *BlockEndState {
|
||||||
|
return s.endState
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *BaseBlockStartState) setEndState(b *BlockEndState) {
|
||||||
|
s.endState = b
|
||||||
|
}
|
||||||
|
|
||||||
|
type BasicBlockStartState struct {
|
||||||
|
*BaseBlockStartState
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBasicBlockStartState() *BasicBlockStartState {
|
||||||
|
b := NewBlockStartState()
|
||||||
|
|
||||||
|
b.stateType = ATNStateBlockStart
|
||||||
|
|
||||||
|
return &BasicBlockStartState{BaseBlockStartState: b}
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ BlockStartState = &BasicBlockStartState{}
|
||||||
|
|
||||||
|
// BlockEndState is a terminal node of a simple (a|b|c) block.
|
||||||
|
type BlockEndState struct {
|
||||||
|
*BaseATNState
|
||||||
|
startState ATNState
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBlockEndState() *BlockEndState {
|
||||||
|
b := NewBaseATNState()
|
||||||
|
|
||||||
|
b.stateType = ATNStateBlockEnd
|
||||||
|
|
||||||
|
return &BlockEndState{BaseATNState: b}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RuleStopState is the last node in the ATN for a rule, unless that rule is the
|
||||||
|
// start symbol. In that case, there is one transition to EOF. Later, we might
|
||||||
|
// encode references to all calls to this rule to compute FOLLOW sets for error
|
||||||
|
// handling.
|
||||||
|
type RuleStopState struct {
|
||||||
|
*BaseATNState
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRuleStopState() *RuleStopState {
|
||||||
|
b := NewBaseATNState()
|
||||||
|
|
||||||
|
b.stateType = ATNStateRuleStop
|
||||||
|
|
||||||
|
return &RuleStopState{BaseATNState: b}
|
||||||
|
}
|
||||||
|
|
||||||
|
type RuleStartState struct {
|
||||||
|
*BaseATNState
|
||||||
|
stopState ATNState
|
||||||
|
isPrecedenceRule bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRuleStartState() *RuleStartState {
|
||||||
|
b := NewBaseATNState()
|
||||||
|
|
||||||
|
b.stateType = ATNStateRuleStart
|
||||||
|
|
||||||
|
return &RuleStartState{BaseATNState: b}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PlusLoopbackState is a decision state for A+ and (A|B)+. It has two
|
||||||
|
// transitions: one to the loop back to start of the block, and one to exit.
|
||||||
|
type PlusLoopbackState struct {
|
||||||
|
*BaseDecisionState
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPlusLoopbackState() *PlusLoopbackState {
|
||||||
|
b := NewBaseDecisionState()
|
||||||
|
|
||||||
|
b.stateType = ATNStatePlusLoopBack
|
||||||
|
|
||||||
|
return &PlusLoopbackState{BaseDecisionState: b}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PlusBlockStartState is the start of a (A|B|...)+ loop. Technically it is a
|
||||||
|
// decision state; we don't use it for code generation. Somebody might need it,
|
||||||
|
// it is included for completeness. In reality, PlusLoopbackState is the real
|
||||||
|
// decision-making node for A+.
|
||||||
|
type PlusBlockStartState struct {
|
||||||
|
*BaseBlockStartState
|
||||||
|
loopBackState ATNState
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPlusBlockStartState() *PlusBlockStartState {
|
||||||
|
b := NewBlockStartState()
|
||||||
|
|
||||||
|
b.stateType = ATNStatePlusBlockStart
|
||||||
|
|
||||||
|
return &PlusBlockStartState{BaseBlockStartState: b}
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ BlockStartState = &PlusBlockStartState{}
|
||||||
|
|
||||||
|
// StarBlockStartState is the block that begins a closure loop.
|
||||||
|
type StarBlockStartState struct {
|
||||||
|
*BaseBlockStartState
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStarBlockStartState() *StarBlockStartState {
|
||||||
|
b := NewBlockStartState()
|
||||||
|
|
||||||
|
b.stateType = ATNStateStarBlockStart
|
||||||
|
|
||||||
|
return &StarBlockStartState{BaseBlockStartState: b}
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ BlockStartState = &StarBlockStartState{}
|
||||||
|
|
||||||
|
type StarLoopbackState struct {
|
||||||
|
*BaseATNState
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStarLoopbackState() *StarLoopbackState {
|
||||||
|
b := NewBaseATNState()
|
||||||
|
|
||||||
|
b.stateType = ATNStateStarLoopBack
|
||||||
|
|
||||||
|
return &StarLoopbackState{BaseATNState: b}
|
||||||
|
}
|
||||||
|
|
||||||
|
type StarLoopEntryState struct {
|
||||||
|
*BaseDecisionState
|
||||||
|
loopBackState ATNState
|
||||||
|
precedenceRuleDecision bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStarLoopEntryState() *StarLoopEntryState {
|
||||||
|
b := NewBaseDecisionState()
|
||||||
|
|
||||||
|
b.stateType = ATNStateStarLoopEntry
|
||||||
|
|
||||||
|
// False precedenceRuleDecision indicates whether s state can benefit from a precedence DFA during SLL decision making.
|
||||||
|
return &StarLoopEntryState{BaseDecisionState: b}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoopEndState marks the end of a * or + loop.
|
||||||
|
type LoopEndState struct {
|
||||||
|
*BaseATNState
|
||||||
|
loopBackState ATNState
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLoopEndState() *LoopEndState {
|
||||||
|
b := NewBaseATNState()
|
||||||
|
|
||||||
|
b.stateType = ATNStateLoopEnd
|
||||||
|
|
||||||
|
return &LoopEndState{BaseATNState: b}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TokensStartState is the Tokens rule start state linking to each lexer rule start state.
|
||||||
|
type TokensStartState struct {
|
||||||
|
*BaseDecisionState
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTokensStartState() *TokensStartState {
|
||||||
|
b := NewBaseDecisionState()
|
||||||
|
|
||||||
|
b.stateType = ATNStateTokenStart
|
||||||
|
|
||||||
|
return &TokensStartState{BaseDecisionState: b}
|
||||||
|
}
|
||||||
11
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/atn_type.go
generated
vendored
Normal file
11
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/atn_type.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
||||||
|
// Use of this file is governed by the BSD 3-clause license that
|
||||||
|
// can be found in the LICENSE.txt file in the project root.
|
||||||
|
|
||||||
|
package antlr
|
||||||
|
|
||||||
|
// Represent the type of recognizer an ATN applies to.
|
||||||
|
const (
|
||||||
|
ATNTypeLexer = 0
|
||||||
|
ATNTypeParser = 1
|
||||||
|
)
|
||||||
12
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/char_stream.go
generated
vendored
Normal file
12
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/char_stream.go
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
||||||
|
// Use of this file is governed by the BSD 3-clause license that
|
||||||
|
// can be found in the LICENSE.txt file in the project root.
|
||||||
|
|
||||||
|
package antlr
|
||||||
|
|
||||||
|
type CharStream interface {
|
||||||
|
IntStream
|
||||||
|
GetText(int, int) string
|
||||||
|
GetTextFromTokens(start, end Token) string
|
||||||
|
GetTextFromInterval(*Interval) string
|
||||||
|
}
|
||||||
56
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/common_token_factory.go
generated
vendored
Normal file
56
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/common_token_factory.go
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
||||||
|
// Use of this file is governed by the BSD 3-clause license that
|
||||||
|
// can be found in the LICENSE.txt file in the project root.
|
||||||
|
|
||||||
|
package antlr
|
||||||
|
|
||||||
|
// TokenFactory creates CommonToken objects.
|
||||||
|
type TokenFactory interface {
|
||||||
|
Create(source *TokenSourceCharStreamPair, ttype int, text string, channel, start, stop, line, column int) Token
|
||||||
|
}
|
||||||
|
|
||||||
|
// CommonTokenFactory is the default TokenFactory implementation.
|
||||||
|
type CommonTokenFactory struct {
|
||||||
|
// copyText indicates whether CommonToken.setText should be called after
|
||||||
|
// constructing tokens to explicitly set the text. This is useful for cases
|
||||||
|
// where the input stream might not be able to provide arbitrary substrings of
|
||||||
|
// text from the input after the lexer creates a token (e.g. the
|
||||||
|
// implementation of CharStream.GetText in UnbufferedCharStream panics an
|
||||||
|
// UnsupportedOperationException). Explicitly setting the token text allows
|
||||||
|
// Token.GetText to be called at any time regardless of the input stream
|
||||||
|
// implementation.
|
||||||
|
//
|
||||||
|
// The default value is false to avoid the performance and memory overhead of
|
||||||
|
// copying text for every token unless explicitly requested.
|
||||||
|
copyText bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCommonTokenFactory(copyText bool) *CommonTokenFactory {
|
||||||
|
return &CommonTokenFactory{copyText: copyText}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CommonTokenFactoryDEFAULT is the default CommonTokenFactory. It does not
|
||||||
|
// explicitly copy token text when constructing tokens.
|
||||||
|
var CommonTokenFactoryDEFAULT = NewCommonTokenFactory(false)
|
||||||
|
|
||||||
|
func (c *CommonTokenFactory) Create(source *TokenSourceCharStreamPair, ttype int, text string, channel, start, stop, line, column int) Token {
|
||||||
|
t := NewCommonToken(source, ttype, channel, start, stop)
|
||||||
|
|
||||||
|
t.line = line
|
||||||
|
t.column = column
|
||||||
|
|
||||||
|
if text != "" {
|
||||||
|
t.SetText(text)
|
||||||
|
} else if c.copyText && source.charStream != nil {
|
||||||
|
t.SetText(source.charStream.GetTextFromInterval(NewInterval(start, stop)))
|
||||||
|
}
|
||||||
|
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommonTokenFactory) createThin(ttype int, text string) Token {
|
||||||
|
t := NewCommonToken(nil, ttype, TokenDefaultChannel, -1, -1)
|
||||||
|
t.SetText(text)
|
||||||
|
|
||||||
|
return t
|
||||||
|
}
|
||||||
449
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/common_token_stream.go
generated
vendored
Normal file
449
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/common_token_stream.go
generated
vendored
Normal file
@@ -0,0 +1,449 @@
|
|||||||
|
// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
||||||
|
// Use of this file is governed by the BSD 3-clause license that
|
||||||
|
// can be found in the LICENSE.txt file in the project root.
|
||||||
|
|
||||||
|
package antlr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CommonTokenStream is an implementation of TokenStream that loads tokens from
|
||||||
|
// a TokenSource on-demand and places the tokens in a buffer to provide access
|
||||||
|
// to any previous token by index. This token stream ignores the value of
|
||||||
|
// Token.getChannel. If your parser requires the token stream filter tokens to
|
||||||
|
// only those on a particular channel, such as Token.DEFAULT_CHANNEL or
|
||||||
|
// Token.HIDDEN_CHANNEL, use a filtering token stream such a CommonTokenStream.
|
||||||
|
type CommonTokenStream struct {
|
||||||
|
channel int
|
||||||
|
|
||||||
|
// fetchedEOF indicates whether the Token.EOF token has been fetched from
|
||||||
|
// tokenSource and added to tokens. This field improves performance for the
|
||||||
|
// following cases:
|
||||||
|
//
|
||||||
|
// consume: The lookahead check in consume to preven consuming the EOF symbol is
|
||||||
|
// optimized by checking the values of fetchedEOF and p instead of calling LA.
|
||||||
|
//
|
||||||
|
// fetch: The check to prevent adding multiple EOF symbols into tokens is
|
||||||
|
// trivial with bt field.
|
||||||
|
fetchedEOF bool
|
||||||
|
|
||||||
|
// index indexs into tokens of the current token (next token to consume).
|
||||||
|
// tokens[p] should be LT(1). It is set to -1 when the stream is first
|
||||||
|
// constructed or when SetTokenSource is called, indicating that the first token
|
||||||
|
// has not yet been fetched from the token source. For additional information,
|
||||||
|
// see the documentation of IntStream for a description of initializing methods.
|
||||||
|
index int
|
||||||
|
|
||||||
|
// tokenSource is the TokenSource from which tokens for the bt stream are
|
||||||
|
// fetched.
|
||||||
|
tokenSource TokenSource
|
||||||
|
|
||||||
|
// tokens is all tokens fetched from the token source. The list is considered a
|
||||||
|
// complete view of the input once fetchedEOF is set to true.
|
||||||
|
tokens []Token
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCommonTokenStream(lexer Lexer, channel int) *CommonTokenStream {
|
||||||
|
return &CommonTokenStream{
|
||||||
|
channel: channel,
|
||||||
|
index: -1,
|
||||||
|
tokenSource: lexer,
|
||||||
|
tokens: make([]Token, 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommonTokenStream) GetAllTokens() []Token {
|
||||||
|
return c.tokens
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommonTokenStream) Mark() int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommonTokenStream) Release(marker int) {}
|
||||||
|
|
||||||
|
func (c *CommonTokenStream) reset() {
|
||||||
|
c.Seek(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommonTokenStream) Seek(index int) {
|
||||||
|
c.lazyInit()
|
||||||
|
c.index = c.adjustSeekIndex(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommonTokenStream) Get(index int) Token {
|
||||||
|
c.lazyInit()
|
||||||
|
|
||||||
|
return c.tokens[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommonTokenStream) Consume() {
|
||||||
|
SkipEOFCheck := false
|
||||||
|
|
||||||
|
if c.index >= 0 {
|
||||||
|
if c.fetchedEOF {
|
||||||
|
// The last token in tokens is EOF. Skip the check if p indexes any fetched.
|
||||||
|
// token except the last.
|
||||||
|
SkipEOFCheck = c.index < len(c.tokens)-1
|
||||||
|
} else {
|
||||||
|
// No EOF token in tokens. Skip the check if p indexes a fetched token.
|
||||||
|
SkipEOFCheck = c.index < len(c.tokens)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Not yet initialized
|
||||||
|
SkipEOFCheck = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if !SkipEOFCheck && c.LA(1) == TokenEOF {
|
||||||
|
panic("cannot consume EOF")
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Sync(c.index + 1) {
|
||||||
|
c.index = c.adjustSeekIndex(c.index + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sync makes sure index i in tokens has a token and returns true if a token is
|
||||||
|
// located at index i and otherwise false.
|
||||||
|
func (c *CommonTokenStream) Sync(i int) bool {
|
||||||
|
n := i - len(c.tokens) + 1 // TODO: How many more elements do we need?
|
||||||
|
|
||||||
|
if n > 0 {
|
||||||
|
fetched := c.fetch(n)
|
||||||
|
return fetched >= n
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetch adds n elements to buffer and returns the actual number of elements
|
||||||
|
// added to the buffer.
|
||||||
|
func (c *CommonTokenStream) fetch(n int) int {
|
||||||
|
if c.fetchedEOF {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
t := c.tokenSource.NextToken()
|
||||||
|
|
||||||
|
t.SetTokenIndex(len(c.tokens))
|
||||||
|
c.tokens = append(c.tokens, t)
|
||||||
|
|
||||||
|
if t.GetTokenType() == TokenEOF {
|
||||||
|
c.fetchedEOF = true
|
||||||
|
|
||||||
|
return i + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTokens gets all tokens from start to stop inclusive.
|
||||||
|
func (c *CommonTokenStream) GetTokens(start int, stop int, types *IntervalSet) []Token {
|
||||||
|
if start < 0 || stop < 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
c.lazyInit()
|
||||||
|
|
||||||
|
subset := make([]Token, 0)
|
||||||
|
|
||||||
|
if stop >= len(c.tokens) {
|
||||||
|
stop = len(c.tokens) - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := start; i < stop; i++ {
|
||||||
|
t := c.tokens[i]
|
||||||
|
|
||||||
|
if t.GetTokenType() == TokenEOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if types == nil || types.contains(t.GetTokenType()) {
|
||||||
|
subset = append(subset, t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return subset
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommonTokenStream) LA(i int) int {
|
||||||
|
return c.LT(i).GetTokenType()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommonTokenStream) lazyInit() {
|
||||||
|
if c.index == -1 {
|
||||||
|
c.setup()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommonTokenStream) setup() {
|
||||||
|
c.Sync(0)
|
||||||
|
c.index = c.adjustSeekIndex(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommonTokenStream) GetTokenSource() TokenSource {
|
||||||
|
return c.tokenSource
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTokenSource resets the c token stream by setting its token source.
|
||||||
|
func (c *CommonTokenStream) SetTokenSource(tokenSource TokenSource) {
|
||||||
|
c.tokenSource = tokenSource
|
||||||
|
c.tokens = make([]Token, 0)
|
||||||
|
c.index = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextTokenOnChannel returns the index of the next token on channel given a
|
||||||
|
// starting index. Returns i if tokens[i] is on channel. Returns -1 if there are
|
||||||
|
// no tokens on channel between i and EOF.
|
||||||
|
func (c *CommonTokenStream) NextTokenOnChannel(i, channel int) int {
|
||||||
|
c.Sync(i)
|
||||||
|
|
||||||
|
if i >= len(c.tokens) {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
token := c.tokens[i]
|
||||||
|
|
||||||
|
for token.GetChannel() != c.channel {
|
||||||
|
if token.GetTokenType() == TokenEOF {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
i++
|
||||||
|
c.Sync(i)
|
||||||
|
token = c.tokens[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
// previousTokenOnChannel returns the index of the previous token on channel
|
||||||
|
// given a starting index. Returns i if tokens[i] is on channel. Returns -1 if
|
||||||
|
// there are no tokens on channel between i and 0.
|
||||||
|
func (c *CommonTokenStream) previousTokenOnChannel(i, channel int) int {
|
||||||
|
for i >= 0 && c.tokens[i].GetChannel() != channel {
|
||||||
|
i--
|
||||||
|
}
|
||||||
|
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHiddenTokensToRight collects all tokens on a specified channel to the
|
||||||
|
// right of the current token up until we see a token on DEFAULT_TOKEN_CHANNEL
|
||||||
|
// or EOF. If channel is -1, it finds any non-default channel token.
|
||||||
|
func (c *CommonTokenStream) GetHiddenTokensToRight(tokenIndex, channel int) []Token {
|
||||||
|
c.lazyInit()
|
||||||
|
|
||||||
|
if tokenIndex < 0 || tokenIndex >= len(c.tokens) {
|
||||||
|
panic(strconv.Itoa(tokenIndex) + " not in 0.." + strconv.Itoa(len(c.tokens)-1))
|
||||||
|
}
|
||||||
|
|
||||||
|
nextOnChannel := c.NextTokenOnChannel(tokenIndex+1, LexerDefaultTokenChannel)
|
||||||
|
from := tokenIndex + 1
|
||||||
|
|
||||||
|
// If no onchannel to the right, then nextOnChannel == -1, so set to to last token
|
||||||
|
var to int
|
||||||
|
|
||||||
|
if nextOnChannel == -1 {
|
||||||
|
to = len(c.tokens) - 1
|
||||||
|
} else {
|
||||||
|
to = nextOnChannel
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.filterForChannel(from, to, channel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHiddenTokensToLeft collects all tokens on channel to the left of the
|
||||||
|
// current token until we see a token on DEFAULT_TOKEN_CHANNEL. If channel is
|
||||||
|
// -1, it finds any non default channel token.
|
||||||
|
func (c *CommonTokenStream) GetHiddenTokensToLeft(tokenIndex, channel int) []Token {
|
||||||
|
c.lazyInit()
|
||||||
|
|
||||||
|
if tokenIndex < 0 || tokenIndex >= len(c.tokens) {
|
||||||
|
panic(strconv.Itoa(tokenIndex) + " not in 0.." + strconv.Itoa(len(c.tokens)-1))
|
||||||
|
}
|
||||||
|
|
||||||
|
prevOnChannel := c.previousTokenOnChannel(tokenIndex-1, LexerDefaultTokenChannel)
|
||||||
|
|
||||||
|
if prevOnChannel == tokenIndex-1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are none on channel to the left and prevOnChannel == -1 then from = 0
|
||||||
|
from := prevOnChannel + 1
|
||||||
|
to := tokenIndex - 1
|
||||||
|
|
||||||
|
return c.filterForChannel(from, to, channel)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommonTokenStream) filterForChannel(left, right, channel int) []Token {
|
||||||
|
hidden := make([]Token, 0)
|
||||||
|
|
||||||
|
for i := left; i < right+1; i++ {
|
||||||
|
t := c.tokens[i]
|
||||||
|
|
||||||
|
if channel == -1 {
|
||||||
|
if t.GetChannel() != LexerDefaultTokenChannel {
|
||||||
|
hidden = append(hidden, t)
|
||||||
|
}
|
||||||
|
} else if t.GetChannel() == channel {
|
||||||
|
hidden = append(hidden, t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(hidden) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return hidden
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommonTokenStream) GetSourceName() string {
|
||||||
|
return c.tokenSource.GetSourceName()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommonTokenStream) Size() int {
|
||||||
|
return len(c.tokens)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommonTokenStream) Index() int {
|
||||||
|
return c.index
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommonTokenStream) GetAllText() string {
|
||||||
|
return c.GetTextFromInterval(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommonTokenStream) GetTextFromTokens(start, end Token) string {
|
||||||
|
if start == nil || end == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.GetTextFromInterval(NewInterval(start.GetTokenIndex(), end.GetTokenIndex()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommonTokenStream) GetTextFromRuleContext(interval RuleContext) string {
|
||||||
|
return c.GetTextFromInterval(interval.GetSourceInterval())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommonTokenStream) GetTextFromInterval(interval *Interval) string {
|
||||||
|
c.lazyInit()
|
||||||
|
|
||||||
|
if interval == nil {
|
||||||
|
c.Fill()
|
||||||
|
interval = NewInterval(0, len(c.tokens)-1)
|
||||||
|
} else {
|
||||||
|
c.Sync(interval.Stop)
|
||||||
|
}
|
||||||
|
|
||||||
|
start := interval.Start
|
||||||
|
stop := interval.Stop
|
||||||
|
|
||||||
|
if start < 0 || stop < 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if stop >= len(c.tokens) {
|
||||||
|
stop = len(c.tokens) - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
s := ""
|
||||||
|
|
||||||
|
for i := start; i < stop+1; i++ {
|
||||||
|
t := c.tokens[i]
|
||||||
|
|
||||||
|
if t.GetTokenType() == TokenEOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
s += t.GetText()
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill gets all tokens from the lexer until EOF.
|
||||||
|
func (c *CommonTokenStream) Fill() {
|
||||||
|
c.lazyInit()
|
||||||
|
|
||||||
|
for c.fetch(1000) == 1000 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommonTokenStream) adjustSeekIndex(i int) int {
|
||||||
|
return c.NextTokenOnChannel(i, c.channel)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommonTokenStream) LB(k int) Token {
|
||||||
|
if k == 0 || c.index-k < 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
i := c.index
|
||||||
|
n := 1
|
||||||
|
|
||||||
|
// Find k good tokens looking backward
|
||||||
|
for n <= k {
|
||||||
|
// Skip off-channel tokens
|
||||||
|
i = c.previousTokenOnChannel(i-1, c.channel)
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
|
||||||
|
if i < 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.tokens[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommonTokenStream) LT(k int) Token {
|
||||||
|
c.lazyInit()
|
||||||
|
|
||||||
|
if k == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if k < 0 {
|
||||||
|
return c.LB(-k)
|
||||||
|
}
|
||||||
|
|
||||||
|
i := c.index
|
||||||
|
n := 1 // We know tokens[n] is valid
|
||||||
|
|
||||||
|
// Find k good tokens
|
||||||
|
for n < k {
|
||||||
|
// Skip off-channel tokens, but make sure to not look past EOF
|
||||||
|
if c.Sync(i + 1) {
|
||||||
|
i = c.NextTokenOnChannel(i+1, c.channel)
|
||||||
|
}
|
||||||
|
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.tokens[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
// getNumberOfOnChannelTokens counts EOF once.
|
||||||
|
func (c *CommonTokenStream) getNumberOfOnChannelTokens() int {
|
||||||
|
var n int
|
||||||
|
|
||||||
|
c.Fill()
|
||||||
|
|
||||||
|
for i := 0; i < len(c.tokens); i++ {
|
||||||
|
t := c.tokens[i]
|
||||||
|
|
||||||
|
if t.GetChannel() == c.channel {
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.GetTokenType() == TokenEOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return n
|
||||||
|
}
|
||||||
147
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/comparators.go
generated
vendored
Normal file
147
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/comparators.go
generated
vendored
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
package antlr
|
||||||
|
|
||||||
|
// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
||||||
|
// Use of this file is governed by the BSD 3-clause license that
|
||||||
|
// can be found in the LICENSE.txt file in the project root.
|
||||||
|
|
||||||
|
// This file contains all the implementations of custom comparators used for generic collections when the
|
||||||
|
// Hash() and Equals() funcs supplied by the struct objects themselves need to be overridden. Normally, we would
|
||||||
|
// put the comparators in the source file for the struct themselves, but given the organization of this code is
|
||||||
|
// sorta kinda based upon the Java code, I found it confusing trying to find out which comparator was where and used by
|
||||||
|
// which instantiation of a collection. For instance, an Array2DHashSet in the Java source, when used with ATNConfig
|
||||||
|
// collections requires three different comparators depending on what the collection is being used for. Collecting - pun intended -
|
||||||
|
// all the comparators here, makes it much easier to see which implementation of hash and equals is used by which collection.
|
||||||
|
// It also makes it easy to verify that the Hash() and Equals() functions marry up with the Java implementations.
|
||||||
|
|
||||||
|
// ObjEqComparator is the equivalent of the Java ObjectEqualityComparator, which is the default instance of
|
||||||
|
// Equality comparator. We do not have inheritance in Go, only interfaces, so we use generics to enforce some
|
||||||
|
// type safety and avoid having to implement this for every type that we want to perform comparison on.
|
||||||
|
//
|
||||||
|
// This comparator works by using the standard Hash() and Equals() methods of the type T that is being compared. Which
|
||||||
|
// allows us to use it in any collection instance that does nto require a special hash or equals implementation.
|
||||||
|
type ObjEqComparator[T Collectable[T]] struct{}
|
||||||
|
|
||||||
|
var (
|
||||||
|
aStateEqInst = &ObjEqComparator[ATNState]{}
|
||||||
|
aConfEqInst = &ObjEqComparator[ATNConfig]{}
|
||||||
|
aConfCompInst = &ATNConfigComparator[ATNConfig]{}
|
||||||
|
atnConfCompInst = &BaseATNConfigComparator[ATNConfig]{}
|
||||||
|
dfaStateEqInst = &ObjEqComparator[*DFAState]{}
|
||||||
|
semctxEqInst = &ObjEqComparator[SemanticContext]{}
|
||||||
|
atnAltCfgEqInst = &ATNAltConfigComparator[ATNConfig]{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Equals2 delegates to the Equals() method of type T
|
||||||
|
func (c *ObjEqComparator[T]) Equals2(o1, o2 T) bool {
|
||||||
|
return o1.Equals(o2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash1 delegates to the Hash() method of type T
|
||||||
|
func (c *ObjEqComparator[T]) Hash1(o T) int {
|
||||||
|
|
||||||
|
return o.Hash()
|
||||||
|
}
|
||||||
|
|
||||||
|
type SemCComparator[T Collectable[T]] struct{}
|
||||||
|
|
||||||
|
// ATNConfigComparator is used as the compartor for the configLookup field of an ATNConfigSet
|
||||||
|
// and has a custom Equals() and Hash() implementation, because equality is not based on the
|
||||||
|
// standard Hash() and Equals() methods of the ATNConfig type.
|
||||||
|
type ATNConfigComparator[T Collectable[T]] struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equals2 is a custom comparator for ATNConfigs specifically for configLookup
|
||||||
|
func (c *ATNConfigComparator[T]) Equals2(o1, o2 ATNConfig) bool {
|
||||||
|
|
||||||
|
// Same pointer, must be equal, even if both nil
|
||||||
|
//
|
||||||
|
if o1 == o2 {
|
||||||
|
return true
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// If either are nil, but not both, then the result is false
|
||||||
|
//
|
||||||
|
if o1 == nil || o2 == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return o1.GetState().GetStateNumber() == o2.GetState().GetStateNumber() &&
|
||||||
|
o1.GetAlt() == o2.GetAlt() &&
|
||||||
|
o1.GetSemanticContext().Equals(o2.GetSemanticContext())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash1 is custom hash implementation for ATNConfigs specifically for configLookup
|
||||||
|
func (c *ATNConfigComparator[T]) Hash1(o ATNConfig) int {
|
||||||
|
hash := 7
|
||||||
|
hash = 31*hash + o.GetState().GetStateNumber()
|
||||||
|
hash = 31*hash + o.GetAlt()
|
||||||
|
hash = 31*hash + o.GetSemanticContext().Hash()
|
||||||
|
return hash
|
||||||
|
}
|
||||||
|
|
||||||
|
// ATNAltConfigComparator is used as the comparator for mapping configs to Alt Bitsets
|
||||||
|
type ATNAltConfigComparator[T Collectable[T]] struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equals2 is a custom comparator for ATNConfigs specifically for configLookup
|
||||||
|
func (c *ATNAltConfigComparator[T]) Equals2(o1, o2 ATNConfig) bool {
|
||||||
|
|
||||||
|
// Same pointer, must be equal, even if both nil
|
||||||
|
//
|
||||||
|
if o1 == o2 {
|
||||||
|
return true
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// If either are nil, but not both, then the result is false
|
||||||
|
//
|
||||||
|
if o1 == nil || o2 == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return o1.GetState().GetStateNumber() == o2.GetState().GetStateNumber() &&
|
||||||
|
o1.GetContext().Equals(o2.GetContext())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash1 is custom hash implementation for ATNConfigs specifically for configLookup
|
||||||
|
func (c *ATNAltConfigComparator[T]) Hash1(o ATNConfig) int {
|
||||||
|
h := murmurInit(7)
|
||||||
|
h = murmurUpdate(h, o.GetState().GetStateNumber())
|
||||||
|
h = murmurUpdate(h, o.GetContext().Hash())
|
||||||
|
return murmurFinish(h, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BaseATNConfigComparator is used as the comparator for the configLookup field of a BaseATNConfigSet
|
||||||
|
// and has a custom Equals() and Hash() implementation, because equality is not based on the
|
||||||
|
// standard Hash() and Equals() methods of the ATNConfig type.
|
||||||
|
type BaseATNConfigComparator[T Collectable[T]] struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equals2 is a custom comparator for ATNConfigs specifically for baseATNConfigSet
|
||||||
|
func (c *BaseATNConfigComparator[T]) Equals2(o1, o2 ATNConfig) bool {
|
||||||
|
|
||||||
|
// Same pointer, must be equal, even if both nil
|
||||||
|
//
|
||||||
|
if o1 == o2 {
|
||||||
|
return true
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// If either are nil, but not both, then the result is false
|
||||||
|
//
|
||||||
|
if o1 == nil || o2 == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return o1.GetState().GetStateNumber() == o2.GetState().GetStateNumber() &&
|
||||||
|
o1.GetAlt() == o2.GetAlt() &&
|
||||||
|
o1.GetSemanticContext().Equals(o2.GetSemanticContext())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash1 is custom hash implementation for ATNConfigs specifically for configLookup, but in fact just
|
||||||
|
// delegates to the standard Hash() method of the ATNConfig type.
|
||||||
|
func (c *BaseATNConfigComparator[T]) Hash1(o ATNConfig) int {
|
||||||
|
|
||||||
|
return o.Hash()
|
||||||
|
}
|
||||||
148
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/dfa.go
generated
vendored
Normal file
148
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/dfa.go
generated
vendored
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
||||||
|
// Use of this file is governed by the BSD 3-clause license that
|
||||||
|
// can be found in the LICENSE.txt file in the project root.
|
||||||
|
|
||||||
|
package antlr
|
||||||
|
|
||||||
|
type DFA struct {
|
||||||
|
// atnStartState is the ATN state in which this was created
|
||||||
|
atnStartState DecisionState
|
||||||
|
|
||||||
|
decision int
|
||||||
|
|
||||||
|
// states is all the DFA states. Use Map to get the old state back; Set can only
|
||||||
|
// indicate whether it is there. Go maps implement key hash collisions and so on and are very
|
||||||
|
// good, but the DFAState is an object and can't be used directly as the key as it can in say JAva
|
||||||
|
// amd C#, whereby if the hashcode is the same for two objects, then Equals() is called against them
|
||||||
|
// to see if they really are the same object.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
states *JStore[*DFAState, *ObjEqComparator[*DFAState]]
|
||||||
|
|
||||||
|
numstates int
|
||||||
|
|
||||||
|
s0 *DFAState
|
||||||
|
|
||||||
|
// precedenceDfa is the backing field for isPrecedenceDfa and setPrecedenceDfa.
|
||||||
|
// True if the DFA is for a precedence decision and false otherwise.
|
||||||
|
precedenceDfa bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDFA(atnStartState DecisionState, decision int) *DFA {
|
||||||
|
dfa := &DFA{
|
||||||
|
atnStartState: atnStartState,
|
||||||
|
decision: decision,
|
||||||
|
states: NewJStore[*DFAState, *ObjEqComparator[*DFAState]](dfaStateEqInst),
|
||||||
|
}
|
||||||
|
if s, ok := atnStartState.(*StarLoopEntryState); ok && s.precedenceRuleDecision {
|
||||||
|
dfa.precedenceDfa = true
|
||||||
|
dfa.s0 = NewDFAState(-1, NewBaseATNConfigSet(false))
|
||||||
|
dfa.s0.isAcceptState = false
|
||||||
|
dfa.s0.requiresFullContext = false
|
||||||
|
}
|
||||||
|
return dfa
|
||||||
|
}
|
||||||
|
|
||||||
|
// getPrecedenceStartState gets the start state for the current precedence and
|
||||||
|
// returns the start state corresponding to the specified precedence if a start
|
||||||
|
// state exists for the specified precedence and nil otherwise. d must be a
|
||||||
|
// precedence DFA. See also isPrecedenceDfa.
|
||||||
|
func (d *DFA) getPrecedenceStartState(precedence int) *DFAState {
|
||||||
|
if !d.getPrecedenceDfa() {
|
||||||
|
panic("only precedence DFAs may contain a precedence start state")
|
||||||
|
}
|
||||||
|
|
||||||
|
// s0.edges is never nil for a precedence DFA
|
||||||
|
if precedence < 0 || precedence >= len(d.getS0().getEdges()) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return d.getS0().getIthEdge(precedence)
|
||||||
|
}
|
||||||
|
|
||||||
|
// setPrecedenceStartState sets the start state for the current precedence. d
|
||||||
|
// must be a precedence DFA. See also isPrecedenceDfa.
|
||||||
|
func (d *DFA) setPrecedenceStartState(precedence int, startState *DFAState) {
|
||||||
|
if !d.getPrecedenceDfa() {
|
||||||
|
panic("only precedence DFAs may contain a precedence start state")
|
||||||
|
}
|
||||||
|
|
||||||
|
if precedence < 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Synchronization on s0 here is ok. When the DFA is turned into a
|
||||||
|
// precedence DFA, s0 will be initialized once and not updated again. s0.edges
|
||||||
|
// is never nil for a precedence DFA.
|
||||||
|
s0 := d.getS0()
|
||||||
|
if precedence >= s0.numEdges() {
|
||||||
|
edges := append(s0.getEdges(), make([]*DFAState, precedence+1-s0.numEdges())...)
|
||||||
|
s0.setEdges(edges)
|
||||||
|
d.setS0(s0)
|
||||||
|
}
|
||||||
|
|
||||||
|
s0.setIthEdge(precedence, startState)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DFA) getPrecedenceDfa() bool {
|
||||||
|
return d.precedenceDfa
|
||||||
|
}
|
||||||
|
|
||||||
|
// setPrecedenceDfa sets whether d is a precedence DFA. If precedenceDfa differs
|
||||||
|
// from the current DFA configuration, then d.states is cleared, the initial
|
||||||
|
// state s0 is set to a new DFAState with an empty outgoing DFAState.edges to
|
||||||
|
// store the start states for individual precedence values if precedenceDfa is
|
||||||
|
// true or nil otherwise, and d.precedenceDfa is updated.
|
||||||
|
func (d *DFA) setPrecedenceDfa(precedenceDfa bool) {
|
||||||
|
if d.getPrecedenceDfa() != precedenceDfa {
|
||||||
|
d.states = NewJStore[*DFAState, *ObjEqComparator[*DFAState]](dfaStateEqInst)
|
||||||
|
d.numstates = 0
|
||||||
|
|
||||||
|
if precedenceDfa {
|
||||||
|
precedenceState := NewDFAState(-1, NewBaseATNConfigSet(false))
|
||||||
|
|
||||||
|
precedenceState.setEdges(make([]*DFAState, 0))
|
||||||
|
precedenceState.isAcceptState = false
|
||||||
|
precedenceState.requiresFullContext = false
|
||||||
|
d.setS0(precedenceState)
|
||||||
|
} else {
|
||||||
|
d.setS0(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
d.precedenceDfa = precedenceDfa
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DFA) getS0() *DFAState {
|
||||||
|
return d.s0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DFA) setS0(s *DFAState) {
|
||||||
|
d.s0 = s
|
||||||
|
}
|
||||||
|
|
||||||
|
// sortedStates returns the states in d sorted by their state number.
|
||||||
|
func (d *DFA) sortedStates() []*DFAState {
|
||||||
|
|
||||||
|
vs := d.states.SortedSlice(func(i, j *DFAState) bool {
|
||||||
|
return i.stateNumber < j.stateNumber
|
||||||
|
})
|
||||||
|
|
||||||
|
return vs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DFA) String(literalNames []string, symbolicNames []string) string {
|
||||||
|
if d.getS0() == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewDFASerializer(d, literalNames, symbolicNames).String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DFA) ToLexerString() string {
|
||||||
|
if d.getS0() == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewLexerDFASerializer(d).String()
|
||||||
|
}
|
||||||
158
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/dfa_serializer.go
generated
vendored
Normal file
158
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/dfa_serializer.go
generated
vendored
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
||||||
|
// Use of this file is governed by the BSD 3-clause license that
|
||||||
|
// can be found in the LICENSE.txt file in the project root.
|
||||||
|
|
||||||
|
package antlr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DFASerializer is a DFA walker that knows how to dump them to serialized
|
||||||
|
// strings.
|
||||||
|
type DFASerializer struct {
|
||||||
|
dfa *DFA
|
||||||
|
literalNames []string
|
||||||
|
symbolicNames []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDFASerializer(dfa *DFA, literalNames, symbolicNames []string) *DFASerializer {
|
||||||
|
if literalNames == nil {
|
||||||
|
literalNames = make([]string, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
if symbolicNames == nil {
|
||||||
|
symbolicNames = make([]string, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &DFASerializer{
|
||||||
|
dfa: dfa,
|
||||||
|
literalNames: literalNames,
|
||||||
|
symbolicNames: symbolicNames,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DFASerializer) String() string {
|
||||||
|
if d.dfa.getS0() == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := ""
|
||||||
|
states := d.dfa.sortedStates()
|
||||||
|
|
||||||
|
for _, s := range states {
|
||||||
|
if s.edges != nil {
|
||||||
|
n := len(s.edges)
|
||||||
|
|
||||||
|
for j := 0; j < n; j++ {
|
||||||
|
t := s.edges[j]
|
||||||
|
|
||||||
|
if t != nil && t.stateNumber != 0x7FFFFFFF {
|
||||||
|
buf += d.GetStateString(s)
|
||||||
|
buf += "-"
|
||||||
|
buf += d.getEdgeLabel(j)
|
||||||
|
buf += "->"
|
||||||
|
buf += d.GetStateString(t)
|
||||||
|
buf += "\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(buf) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DFASerializer) getEdgeLabel(i int) string {
|
||||||
|
if i == 0 {
|
||||||
|
return "EOF"
|
||||||
|
} else if d.literalNames != nil && i-1 < len(d.literalNames) {
|
||||||
|
return d.literalNames[i-1]
|
||||||
|
} else if d.symbolicNames != nil && i-1 < len(d.symbolicNames) {
|
||||||
|
return d.symbolicNames[i-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
return strconv.Itoa(i - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DFASerializer) GetStateString(s *DFAState) string {
|
||||||
|
var a, b string
|
||||||
|
|
||||||
|
if s.isAcceptState {
|
||||||
|
a = ":"
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.requiresFullContext {
|
||||||
|
b = "^"
|
||||||
|
}
|
||||||
|
|
||||||
|
baseStateStr := a + "s" + strconv.Itoa(s.stateNumber) + b
|
||||||
|
|
||||||
|
if s.isAcceptState {
|
||||||
|
if s.predicates != nil {
|
||||||
|
return baseStateStr + "=>" + fmt.Sprint(s.predicates)
|
||||||
|
}
|
||||||
|
|
||||||
|
return baseStateStr + "=>" + fmt.Sprint(s.prediction)
|
||||||
|
}
|
||||||
|
|
||||||
|
return baseStateStr
|
||||||
|
}
|
||||||
|
|
||||||
|
type LexerDFASerializer struct {
|
||||||
|
*DFASerializer
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLexerDFASerializer(dfa *DFA) *LexerDFASerializer {
|
||||||
|
return &LexerDFASerializer{DFASerializer: NewDFASerializer(dfa, nil, nil)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LexerDFASerializer) getEdgeLabel(i int) string {
|
||||||
|
var sb strings.Builder
|
||||||
|
sb.Grow(6)
|
||||||
|
sb.WriteByte('\'')
|
||||||
|
sb.WriteRune(rune(i))
|
||||||
|
sb.WriteByte('\'')
|
||||||
|
return sb.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LexerDFASerializer) String() string {
|
||||||
|
if l.dfa.getS0() == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := ""
|
||||||
|
states := l.dfa.sortedStates()
|
||||||
|
|
||||||
|
for i := 0; i < len(states); i++ {
|
||||||
|
s := states[i]
|
||||||
|
|
||||||
|
if s.edges != nil {
|
||||||
|
n := len(s.edges)
|
||||||
|
|
||||||
|
for j := 0; j < n; j++ {
|
||||||
|
t := s.edges[j]
|
||||||
|
|
||||||
|
if t != nil && t.stateNumber != 0x7FFFFFFF {
|
||||||
|
buf += l.GetStateString(s)
|
||||||
|
buf += "-"
|
||||||
|
buf += l.getEdgeLabel(j)
|
||||||
|
buf += "->"
|
||||||
|
buf += l.GetStateString(t)
|
||||||
|
buf += "\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(buf) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf
|
||||||
|
}
|
||||||
169
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/dfa_state.go
generated
vendored
Normal file
169
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/dfa_state.go
generated
vendored
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
||||||
|
// Use of this file is governed by the BSD 3-clause license that
|
||||||
|
// can be found in the LICENSE.txt file in the project root.
|
||||||
|
|
||||||
|
package antlr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PredPrediction maps a predicate to a predicted alternative.
|
||||||
|
type PredPrediction struct {
|
||||||
|
alt int
|
||||||
|
pred SemanticContext
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPredPrediction(pred SemanticContext, alt int) *PredPrediction {
|
||||||
|
return &PredPrediction{alt: alt, pred: pred}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PredPrediction) String() string {
|
||||||
|
return "(" + fmt.Sprint(p.pred) + ", " + fmt.Sprint(p.alt) + ")"
|
||||||
|
}
|
||||||
|
|
||||||
|
// DFAState represents a set of possible ATN configurations. As Aho, Sethi,
|
||||||
|
// Ullman p. 117 says: "The DFA uses its state to keep track of all possible
|
||||||
|
// states the ATN can be in after reading each input symbol. That is to say,
|
||||||
|
// after reading input a1a2..an, the DFA is in a state that represents the
|
||||||
|
// subset T of the states of the ATN that are reachable from the ATN's start
|
||||||
|
// state along some path labeled a1a2..an." In conventional NFA-to-DFA
|
||||||
|
// conversion, therefore, the subset T would be a bitset representing the set of
|
||||||
|
// states the ATN could be in. We need to track the alt predicted by each state
|
||||||
|
// as well, however. More importantly, we need to maintain a stack of states,
|
||||||
|
// tracking the closure operations as they jump from rule to rule, emulating
|
||||||
|
// rule invocations (method calls). I have to add a stack to simulate the proper
|
||||||
|
// lookahead sequences for the underlying LL grammar from which the ATN was
|
||||||
|
// derived.
|
||||||
|
//
|
||||||
|
// I use a set of ATNConfig objects, not simple states. An ATNConfig is both a
|
||||||
|
// state (ala normal conversion) and a RuleContext describing the chain of rules
|
||||||
|
// (if any) followed to arrive at that state.
|
||||||
|
//
|
||||||
|
// A DFAState may have multiple references to a particular state, but with
|
||||||
|
// different ATN contexts (with same or different alts) meaning that state was
|
||||||
|
// reached via a different set of rule invocations.
|
||||||
|
type DFAState struct {
|
||||||
|
stateNumber int
|
||||||
|
configs ATNConfigSet
|
||||||
|
|
||||||
|
// edges elements point to the target of the symbol. Shift up by 1 so (-1)
|
||||||
|
// Token.EOF maps to the first element.
|
||||||
|
edges []*DFAState
|
||||||
|
|
||||||
|
isAcceptState bool
|
||||||
|
|
||||||
|
// prediction is the ttype we match or alt we predict if the state is accept.
|
||||||
|
// Set to ATN.INVALID_ALT_NUMBER when predicates != nil or
|
||||||
|
// requiresFullContext.
|
||||||
|
prediction int
|
||||||
|
|
||||||
|
lexerActionExecutor *LexerActionExecutor
|
||||||
|
|
||||||
|
// requiresFullContext indicates it was created during an SLL prediction that
|
||||||
|
// discovered a conflict between the configurations in the state. Future
|
||||||
|
// ParserATNSimulator.execATN invocations immediately jump doing
|
||||||
|
// full context prediction if true.
|
||||||
|
requiresFullContext bool
|
||||||
|
|
||||||
|
// predicates is the predicates associated with the ATN configurations of the
|
||||||
|
// DFA state during SLL parsing. When we have predicates, requiresFullContext
|
||||||
|
// is false, since full context prediction evaluates predicates on-the-fly. If
|
||||||
|
// d is
|
||||||
|
// not nil, then prediction is ATN.INVALID_ALT_NUMBER.
|
||||||
|
//
|
||||||
|
// We only use these for non-requiresFullContext but conflicting states. That
|
||||||
|
// means we know from the context (it's $ or we don't dip into outer context)
|
||||||
|
// that it's an ambiguity not a conflict.
|
||||||
|
//
|
||||||
|
// This list is computed by
|
||||||
|
// ParserATNSimulator.predicateDFAState.
|
||||||
|
predicates []*PredPrediction
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDFAState(stateNumber int, configs ATNConfigSet) *DFAState {
|
||||||
|
if configs == nil {
|
||||||
|
configs = NewBaseATNConfigSet(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &DFAState{configs: configs, stateNumber: stateNumber}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAltSet gets the set of all alts mentioned by all ATN configurations in d.
|
||||||
|
func (d *DFAState) GetAltSet() []int {
|
||||||
|
var alts []int
|
||||||
|
|
||||||
|
if d.configs != nil {
|
||||||
|
for _, c := range d.configs.GetItems() {
|
||||||
|
alts = append(alts, c.GetAlt())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(alts) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return alts
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DFAState) getEdges() []*DFAState {
|
||||||
|
return d.edges
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DFAState) numEdges() int {
|
||||||
|
return len(d.edges)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DFAState) getIthEdge(i int) *DFAState {
|
||||||
|
return d.edges[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DFAState) setEdges(newEdges []*DFAState) {
|
||||||
|
d.edges = newEdges
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DFAState) setIthEdge(i int, edge *DFAState) {
|
||||||
|
d.edges[i] = edge
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DFAState) setPrediction(v int) {
|
||||||
|
d.prediction = v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DFAState) String() string {
|
||||||
|
var s string
|
||||||
|
if d.isAcceptState {
|
||||||
|
if d.predicates != nil {
|
||||||
|
s = "=>" + fmt.Sprint(d.predicates)
|
||||||
|
} else {
|
||||||
|
s = "=>" + fmt.Sprint(d.prediction)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%d:%s%s", d.stateNumber, fmt.Sprint(d.configs), s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DFAState) Hash() int {
|
||||||
|
h := murmurInit(7)
|
||||||
|
h = murmurUpdate(h, d.configs.Hash())
|
||||||
|
return murmurFinish(h, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equals returns whether d equals other. Two DFAStates are equal if their ATN
|
||||||
|
// configuration sets are the same. This method is used to see if a state
|
||||||
|
// already exists.
|
||||||
|
//
|
||||||
|
// Because the number of alternatives and number of ATN configurations are
|
||||||
|
// finite, there is a finite number of DFA states that can be processed. This is
|
||||||
|
// necessary to show that the algorithm terminates.
|
||||||
|
//
|
||||||
|
// Cannot test the DFA state numbers here because in
|
||||||
|
// ParserATNSimulator.addDFAState we need to know if any other state exists that
|
||||||
|
// has d exact set of ATN configurations. The stateNumber is irrelevant.
|
||||||
|
func (d *DFAState) Equals(o Collectable[*DFAState]) bool {
|
||||||
|
if d == o {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return d.configs.Equals(o.(*DFAState).configs)
|
||||||
|
}
|
||||||
109
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/diagnostic_error_listener.go
generated
vendored
Normal file
109
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/diagnostic_error_listener.go
generated
vendored
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
||||||
|
// Use of this file is governed by the BSD 3-clause license that
|
||||||
|
// can be found in the LICENSE.txt file in the project root.
|
||||||
|
|
||||||
|
package antlr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
//
|
||||||
|
// This implementation of {@link ANTLRErrorListener} can be used to identify
|
||||||
|
// certain potential correctness and performance problems in grammars. "reports"
|
||||||
|
// are made by calling {@link Parser//NotifyErrorListeners} with the appropriate
|
||||||
|
// message.
|
||||||
|
//
|
||||||
|
// <ul>
|
||||||
|
// <li><b>Ambiguities</b>: These are cases where more than one path through the
|
||||||
|
// grammar can Match the input.</li>
|
||||||
|
// <li><b>Weak context sensitivity</b>: These are cases where full-context
|
||||||
|
// prediction resolved an SLL conflict to a unique alternative which equaled the
|
||||||
|
// minimum alternative of the SLL conflict.</li>
|
||||||
|
// <li><b>Strong (forced) context sensitivity</b>: These are cases where the
|
||||||
|
// full-context prediction resolved an SLL conflict to a unique alternative,
|
||||||
|
// <em>and</em> the minimum alternative of the SLL conflict was found to not be
|
||||||
|
// a truly viable alternative. Two-stage parsing cannot be used for inputs where
|
||||||
|
// d situation occurs.</li>
|
||||||
|
// </ul>
|
||||||
|
|
||||||
|
type DiagnosticErrorListener struct {
|
||||||
|
*DefaultErrorListener
|
||||||
|
|
||||||
|
exactOnly bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDiagnosticErrorListener(exactOnly bool) *DiagnosticErrorListener {
|
||||||
|
|
||||||
|
n := new(DiagnosticErrorListener)
|
||||||
|
|
||||||
|
// whether all ambiguities or only exact ambiguities are Reported.
|
||||||
|
n.exactOnly = exactOnly
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DiagnosticErrorListener) ReportAmbiguity(recognizer Parser, dfa *DFA, startIndex, stopIndex int, exact bool, ambigAlts *BitSet, configs ATNConfigSet) {
|
||||||
|
if d.exactOnly && !exact {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
msg := "reportAmbiguity d=" +
|
||||||
|
d.getDecisionDescription(recognizer, dfa) +
|
||||||
|
": ambigAlts=" +
|
||||||
|
d.getConflictingAlts(ambigAlts, configs).String() +
|
||||||
|
", input='" +
|
||||||
|
recognizer.GetTokenStream().GetTextFromInterval(NewInterval(startIndex, stopIndex)) + "'"
|
||||||
|
recognizer.NotifyErrorListeners(msg, nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DiagnosticErrorListener) ReportAttemptingFullContext(recognizer Parser, dfa *DFA, startIndex, stopIndex int, conflictingAlts *BitSet, configs ATNConfigSet) {
|
||||||
|
|
||||||
|
msg := "reportAttemptingFullContext d=" +
|
||||||
|
d.getDecisionDescription(recognizer, dfa) +
|
||||||
|
", input='" +
|
||||||
|
recognizer.GetTokenStream().GetTextFromInterval(NewInterval(startIndex, stopIndex)) + "'"
|
||||||
|
recognizer.NotifyErrorListeners(msg, nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DiagnosticErrorListener) ReportContextSensitivity(recognizer Parser, dfa *DFA, startIndex, stopIndex, prediction int, configs ATNConfigSet) {
|
||||||
|
msg := "reportContextSensitivity d=" +
|
||||||
|
d.getDecisionDescription(recognizer, dfa) +
|
||||||
|
", input='" +
|
||||||
|
recognizer.GetTokenStream().GetTextFromInterval(NewInterval(startIndex, stopIndex)) + "'"
|
||||||
|
recognizer.NotifyErrorListeners(msg, nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DiagnosticErrorListener) getDecisionDescription(recognizer Parser, dfa *DFA) string {
|
||||||
|
decision := dfa.decision
|
||||||
|
ruleIndex := dfa.atnStartState.GetRuleIndex()
|
||||||
|
|
||||||
|
ruleNames := recognizer.GetRuleNames()
|
||||||
|
if ruleIndex < 0 || ruleIndex >= len(ruleNames) {
|
||||||
|
return strconv.Itoa(decision)
|
||||||
|
}
|
||||||
|
ruleName := ruleNames[ruleIndex]
|
||||||
|
if ruleName == "" {
|
||||||
|
return strconv.Itoa(decision)
|
||||||
|
}
|
||||||
|
return strconv.Itoa(decision) + " (" + ruleName + ")"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Computes the set of conflicting or ambiguous alternatives from a
|
||||||
|
// configuration set, if that information was not already provided by the
|
||||||
|
// parser.
|
||||||
|
//
|
||||||
|
// @param ReportedAlts The set of conflicting or ambiguous alternatives, as
|
||||||
|
// Reported by the parser.
|
||||||
|
// @param configs The conflicting or ambiguous configuration set.
|
||||||
|
// @return Returns {@code ReportedAlts} if it is not {@code nil}, otherwise
|
||||||
|
// returns the set of alternatives represented in {@code configs}.
|
||||||
|
func (d *DiagnosticErrorListener) getConflictingAlts(ReportedAlts *BitSet, set ATNConfigSet) *BitSet {
|
||||||
|
if ReportedAlts != nil {
|
||||||
|
return ReportedAlts
|
||||||
|
}
|
||||||
|
result := NewBitSet()
|
||||||
|
for _, c := range set.GetItems() {
|
||||||
|
result.add(c.GetAlt())
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
104
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/error_listener.go
generated
vendored
Normal file
104
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/error_listener.go
generated
vendored
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
||||||
|
// Use of this file is governed by the BSD 3-clause license that
|
||||||
|
// can be found in the LICENSE.txt file in the project root.
|
||||||
|
|
||||||
|
package antlr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Provides an empty default implementation of {@link ANTLRErrorListener}. The
|
||||||
|
// default implementation of each method does nothing, but can be overridden as
|
||||||
|
// necessary.
|
||||||
|
|
||||||
|
type ErrorListener interface {
|
||||||
|
SyntaxError(recognizer Recognizer, offendingSymbol interface{}, line, column int, msg string, e RecognitionException)
|
||||||
|
ReportAmbiguity(recognizer Parser, dfa *DFA, startIndex, stopIndex int, exact bool, ambigAlts *BitSet, configs ATNConfigSet)
|
||||||
|
ReportAttemptingFullContext(recognizer Parser, dfa *DFA, startIndex, stopIndex int, conflictingAlts *BitSet, configs ATNConfigSet)
|
||||||
|
ReportContextSensitivity(recognizer Parser, dfa *DFA, startIndex, stopIndex, prediction int, configs ATNConfigSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
type DefaultErrorListener struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDefaultErrorListener() *DefaultErrorListener {
|
||||||
|
return new(DefaultErrorListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DefaultErrorListener) SyntaxError(recognizer Recognizer, offendingSymbol interface{}, line, column int, msg string, e RecognitionException) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DefaultErrorListener) ReportAmbiguity(recognizer Parser, dfa *DFA, startIndex, stopIndex int, exact bool, ambigAlts *BitSet, configs ATNConfigSet) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DefaultErrorListener) ReportAttemptingFullContext(recognizer Parser, dfa *DFA, startIndex, stopIndex int, conflictingAlts *BitSet, configs ATNConfigSet) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DefaultErrorListener) ReportContextSensitivity(recognizer Parser, dfa *DFA, startIndex, stopIndex, prediction int, configs ATNConfigSet) {
|
||||||
|
}
|
||||||
|
|
||||||
|
type ConsoleErrorListener struct {
|
||||||
|
*DefaultErrorListener
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConsoleErrorListener() *ConsoleErrorListener {
|
||||||
|
return new(ConsoleErrorListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provides a default instance of {@link ConsoleErrorListener}.
|
||||||
|
var ConsoleErrorListenerINSTANCE = NewConsoleErrorListener()
|
||||||
|
|
||||||
|
// {@inheritDoc}
|
||||||
|
//
|
||||||
|
// <p>
|
||||||
|
// This implementation prints messages to {@link System//err} containing the
|
||||||
|
// values of {@code line}, {@code charPositionInLine}, and {@code msg} using
|
||||||
|
// the following format.</p>
|
||||||
|
//
|
||||||
|
// <pre>
|
||||||
|
// line <em>line</em>:<em>charPositionInLine</em> <em>msg</em>
|
||||||
|
// </pre>
|
||||||
|
func (c *ConsoleErrorListener) SyntaxError(recognizer Recognizer, offendingSymbol interface{}, line, column int, msg string, e RecognitionException) {
|
||||||
|
fmt.Fprintln(os.Stderr, "line "+strconv.Itoa(line)+":"+strconv.Itoa(column)+" "+msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProxyErrorListener struct {
|
||||||
|
*DefaultErrorListener
|
||||||
|
delegates []ErrorListener
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewProxyErrorListener(delegates []ErrorListener) *ProxyErrorListener {
|
||||||
|
if delegates == nil {
|
||||||
|
panic("delegates is not provided")
|
||||||
|
}
|
||||||
|
l := new(ProxyErrorListener)
|
||||||
|
l.delegates = delegates
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ProxyErrorListener) SyntaxError(recognizer Recognizer, offendingSymbol interface{}, line, column int, msg string, e RecognitionException) {
|
||||||
|
for _, d := range p.delegates {
|
||||||
|
d.SyntaxError(recognizer, offendingSymbol, line, column, msg, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ProxyErrorListener) ReportAmbiguity(recognizer Parser, dfa *DFA, startIndex, stopIndex int, exact bool, ambigAlts *BitSet, configs ATNConfigSet) {
|
||||||
|
for _, d := range p.delegates {
|
||||||
|
d.ReportAmbiguity(recognizer, dfa, startIndex, stopIndex, exact, ambigAlts, configs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ProxyErrorListener) ReportAttemptingFullContext(recognizer Parser, dfa *DFA, startIndex, stopIndex int, conflictingAlts *BitSet, configs ATNConfigSet) {
|
||||||
|
for _, d := range p.delegates {
|
||||||
|
d.ReportAttemptingFullContext(recognizer, dfa, startIndex, stopIndex, conflictingAlts, configs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ProxyErrorListener) ReportContextSensitivity(recognizer Parser, dfa *DFA, startIndex, stopIndex, prediction int, configs ATNConfigSet) {
|
||||||
|
for _, d := range p.delegates {
|
||||||
|
d.ReportContextSensitivity(recognizer, dfa, startIndex, stopIndex, prediction, configs)
|
||||||
|
}
|
||||||
|
}
|
||||||
734
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/error_strategy.go
generated
vendored
Normal file
734
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/error_strategy.go
generated
vendored
Normal file
@@ -0,0 +1,734 @@
|
|||||||
|
// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
||||||
|
// Use of this file is governed by the BSD 3-clause license that
|
||||||
|
// can be found in the LICENSE.txt file in the project root.
|
||||||
|
|
||||||
|
package antlr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ErrorStrategy interface {
|
||||||
|
reset(Parser)
|
||||||
|
RecoverInline(Parser) Token
|
||||||
|
Recover(Parser, RecognitionException)
|
||||||
|
Sync(Parser)
|
||||||
|
InErrorRecoveryMode(Parser) bool
|
||||||
|
ReportError(Parser, RecognitionException)
|
||||||
|
ReportMatch(Parser)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the default implementation of {@link ANTLRErrorStrategy} used for
|
||||||
|
// error Reporting and recovery in ANTLR parsers.
|
||||||
|
type DefaultErrorStrategy struct {
|
||||||
|
errorRecoveryMode bool
|
||||||
|
lastErrorIndex int
|
||||||
|
lastErrorStates *IntervalSet
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ ErrorStrategy = &DefaultErrorStrategy{}
|
||||||
|
|
||||||
|
func NewDefaultErrorStrategy() *DefaultErrorStrategy {
|
||||||
|
|
||||||
|
d := new(DefaultErrorStrategy)
|
||||||
|
|
||||||
|
// Indicates whether the error strategy is currently "recovering from an
|
||||||
|
// error". This is used to suppress Reporting multiple error messages while
|
||||||
|
// attempting to recover from a detected syntax error.
|
||||||
|
//
|
||||||
|
// @see //InErrorRecoveryMode
|
||||||
|
//
|
||||||
|
d.errorRecoveryMode = false
|
||||||
|
|
||||||
|
// The index into the input stream where the last error occurred.
|
||||||
|
// This is used to prevent infinite loops where an error is found
|
||||||
|
// but no token is consumed during recovery...another error is found,
|
||||||
|
// ad nauseum. This is a failsafe mechanism to guarantee that at least
|
||||||
|
// one token/tree node is consumed for two errors.
|
||||||
|
//
|
||||||
|
d.lastErrorIndex = -1
|
||||||
|
d.lastErrorStates = nil
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
// <p>The default implementation simply calls {@link //endErrorCondition} to
|
||||||
|
// ensure that the handler is not in error recovery mode.</p>
|
||||||
|
func (d *DefaultErrorStrategy) reset(recognizer Parser) {
|
||||||
|
d.endErrorCondition(recognizer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This method is called to enter error recovery mode when a recognition
|
||||||
|
// exception is Reported.
|
||||||
|
//
|
||||||
|
// @param recognizer the parser instance
|
||||||
|
func (d *DefaultErrorStrategy) beginErrorCondition(recognizer Parser) {
|
||||||
|
d.errorRecoveryMode = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DefaultErrorStrategy) InErrorRecoveryMode(recognizer Parser) bool {
|
||||||
|
return d.errorRecoveryMode
|
||||||
|
}
|
||||||
|
|
||||||
|
// This method is called to leave error recovery mode after recovering from
|
||||||
|
// a recognition exception.
|
||||||
|
//
|
||||||
|
// @param recognizer
|
||||||
|
func (d *DefaultErrorStrategy) endErrorCondition(recognizer Parser) {
|
||||||
|
d.errorRecoveryMode = false
|
||||||
|
d.lastErrorStates = nil
|
||||||
|
d.lastErrorIndex = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// {@inheritDoc}
|
||||||
|
//
|
||||||
|
// <p>The default implementation simply calls {@link //endErrorCondition}.</p>
|
||||||
|
func (d *DefaultErrorStrategy) ReportMatch(recognizer Parser) {
|
||||||
|
d.endErrorCondition(recognizer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// {@inheritDoc}
|
||||||
|
//
|
||||||
|
// <p>The default implementation returns immediately if the handler is already
|
||||||
|
// in error recovery mode. Otherwise, it calls {@link //beginErrorCondition}
|
||||||
|
// and dispatches the Reporting task based on the runtime type of {@code e}
|
||||||
|
// according to the following table.</p>
|
||||||
|
//
|
||||||
|
// <ul>
|
||||||
|
// <li>{@link NoViableAltException}: Dispatches the call to
|
||||||
|
// {@link //ReportNoViableAlternative}</li>
|
||||||
|
// <li>{@link InputMisMatchException}: Dispatches the call to
|
||||||
|
// {@link //ReportInputMisMatch}</li>
|
||||||
|
// <li>{@link FailedPredicateException}: Dispatches the call to
|
||||||
|
// {@link //ReportFailedPredicate}</li>
|
||||||
|
// <li>All other types: calls {@link Parser//NotifyErrorListeners} to Report
|
||||||
|
// the exception</li>
|
||||||
|
// </ul>
|
||||||
|
func (d *DefaultErrorStrategy) ReportError(recognizer Parser, e RecognitionException) {
|
||||||
|
// if we've already Reported an error and have not Matched a token
|
||||||
|
// yet successfully, don't Report any errors.
|
||||||
|
if d.InErrorRecoveryMode(recognizer) {
|
||||||
|
return // don't Report spurious errors
|
||||||
|
}
|
||||||
|
d.beginErrorCondition(recognizer)
|
||||||
|
|
||||||
|
switch t := e.(type) {
|
||||||
|
default:
|
||||||
|
fmt.Println("unknown recognition error type: " + reflect.TypeOf(e).Name())
|
||||||
|
// fmt.Println(e.stack)
|
||||||
|
recognizer.NotifyErrorListeners(e.GetMessage(), e.GetOffendingToken(), e)
|
||||||
|
case *NoViableAltException:
|
||||||
|
d.ReportNoViableAlternative(recognizer, t)
|
||||||
|
case *InputMisMatchException:
|
||||||
|
d.ReportInputMisMatch(recognizer, t)
|
||||||
|
case *FailedPredicateException:
|
||||||
|
d.ReportFailedPredicate(recognizer, t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// {@inheritDoc}
|
||||||
|
//
|
||||||
|
// <p>The default implementation reSynchronizes the parser by consuming tokens
|
||||||
|
// until we find one in the reSynchronization set--loosely the set of tokens
|
||||||
|
// that can follow the current rule.</p>
|
||||||
|
func (d *DefaultErrorStrategy) Recover(recognizer Parser, e RecognitionException) {
|
||||||
|
|
||||||
|
if d.lastErrorIndex == recognizer.GetInputStream().Index() &&
|
||||||
|
d.lastErrorStates != nil && d.lastErrorStates.contains(recognizer.GetState()) {
|
||||||
|
// uh oh, another error at same token index and previously-Visited
|
||||||
|
// state in ATN must be a case where LT(1) is in the recovery
|
||||||
|
// token set so nothing got consumed. Consume a single token
|
||||||
|
// at least to prevent an infinite loop d is a failsafe.
|
||||||
|
recognizer.Consume()
|
||||||
|
}
|
||||||
|
d.lastErrorIndex = recognizer.GetInputStream().Index()
|
||||||
|
if d.lastErrorStates == nil {
|
||||||
|
d.lastErrorStates = NewIntervalSet()
|
||||||
|
}
|
||||||
|
d.lastErrorStates.addOne(recognizer.GetState())
|
||||||
|
followSet := d.getErrorRecoverySet(recognizer)
|
||||||
|
d.consumeUntil(recognizer, followSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The default implementation of {@link ANTLRErrorStrategy//Sync} makes sure
|
||||||
|
// that the current lookahead symbol is consistent with what were expecting
|
||||||
|
// at d point in the ATN. You can call d anytime but ANTLR only
|
||||||
|
// generates code to check before subrules/loops and each iteration.
|
||||||
|
//
|
||||||
|
// <p>Implements Jim Idle's magic Sync mechanism in closures and optional
|
||||||
|
// subrules. E.g.,</p>
|
||||||
|
//
|
||||||
|
// <pre>
|
||||||
|
// a : Sync ( stuff Sync )*
|
||||||
|
// Sync : {consume to what can follow Sync}
|
||||||
|
// </pre>
|
||||||
|
//
|
||||||
|
// At the start of a sub rule upon error, {@link //Sync} performs single
|
||||||
|
// token deletion, if possible. If it can't do that, it bails on the current
|
||||||
|
// rule and uses the default error recovery, which consumes until the
|
||||||
|
// reSynchronization set of the current rule.
|
||||||
|
//
|
||||||
|
// <p>If the sub rule is optional ({@code (...)?}, {@code (...)*}, or block
|
||||||
|
// with an empty alternative), then the expected set includes what follows
|
||||||
|
// the subrule.</p>
|
||||||
|
//
|
||||||
|
// <p>During loop iteration, it consumes until it sees a token that can start a
|
||||||
|
// sub rule or what follows loop. Yes, that is pretty aggressive. We opt to
|
||||||
|
// stay in the loop as long as possible.</p>
|
||||||
|
//
|
||||||
|
// <p><strong>ORIGINS</strong></p>
|
||||||
|
//
|
||||||
|
// <p>Previous versions of ANTLR did a poor job of their recovery within loops.
|
||||||
|
// A single mismatch token or missing token would force the parser to bail
|
||||||
|
// out of the entire rules surrounding the loop. So, for rule</p>
|
||||||
|
//
|
||||||
|
// <pre>
|
||||||
|
// classfunc : 'class' ID '{' member* '}'
|
||||||
|
// </pre>
|
||||||
|
//
|
||||||
|
// input with an extra token between members would force the parser to
|
||||||
|
// consume until it found the next class definition rather than the next
|
||||||
|
// member definition of the current class.
|
||||||
|
//
|
||||||
|
// <p>This functionality cost a little bit of effort because the parser has to
|
||||||
|
// compare token set at the start of the loop and at each iteration. If for
|
||||||
|
// some reason speed is suffering for you, you can turn off d
|
||||||
|
// functionality by simply overriding d method as a blank { }.</p>
|
||||||
|
func (d *DefaultErrorStrategy) Sync(recognizer Parser) {
|
||||||
|
// If already recovering, don't try to Sync
|
||||||
|
if d.InErrorRecoveryMode(recognizer) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s := recognizer.GetInterpreter().atn.states[recognizer.GetState()]
|
||||||
|
la := recognizer.GetTokenStream().LA(1)
|
||||||
|
|
||||||
|
// try cheaper subset first might get lucky. seems to shave a wee bit off
|
||||||
|
nextTokens := recognizer.GetATN().NextTokens(s, nil)
|
||||||
|
if nextTokens.contains(TokenEpsilon) || nextTokens.contains(la) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch s.GetStateType() {
|
||||||
|
case ATNStateBlockStart, ATNStateStarBlockStart, ATNStatePlusBlockStart, ATNStateStarLoopEntry:
|
||||||
|
// Report error and recover if possible
|
||||||
|
if d.SingleTokenDeletion(recognizer) != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
panic(NewInputMisMatchException(recognizer))
|
||||||
|
case ATNStatePlusLoopBack, ATNStateStarLoopBack:
|
||||||
|
d.ReportUnwantedToken(recognizer)
|
||||||
|
expecting := NewIntervalSet()
|
||||||
|
expecting.addSet(recognizer.GetExpectedTokens())
|
||||||
|
whatFollowsLoopIterationOrRule := expecting.addSet(d.getErrorRecoverySet(recognizer))
|
||||||
|
d.consumeUntil(recognizer, whatFollowsLoopIterationOrRule)
|
||||||
|
default:
|
||||||
|
// do nothing if we can't identify the exact kind of ATN state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is called by {@link //ReportError} when the exception is a
|
||||||
|
// {@link NoViableAltException}.
|
||||||
|
//
|
||||||
|
// @see //ReportError
|
||||||
|
//
|
||||||
|
// @param recognizer the parser instance
|
||||||
|
// @param e the recognition exception
|
||||||
|
func (d *DefaultErrorStrategy) ReportNoViableAlternative(recognizer Parser, e *NoViableAltException) {
|
||||||
|
tokens := recognizer.GetTokenStream()
|
||||||
|
var input string
|
||||||
|
if tokens != nil {
|
||||||
|
if e.startToken.GetTokenType() == TokenEOF {
|
||||||
|
input = "<EOF>"
|
||||||
|
} else {
|
||||||
|
input = tokens.GetTextFromTokens(e.startToken, e.offendingToken)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
input = "<unknown input>"
|
||||||
|
}
|
||||||
|
msg := "no viable alternative at input " + d.escapeWSAndQuote(input)
|
||||||
|
recognizer.NotifyErrorListeners(msg, e.offendingToken, e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is called by {@link //ReportError} when the exception is an
|
||||||
|
// {@link InputMisMatchException}.
|
||||||
|
//
|
||||||
|
// @see //ReportError
|
||||||
|
//
|
||||||
|
// @param recognizer the parser instance
|
||||||
|
// @param e the recognition exception
|
||||||
|
func (this *DefaultErrorStrategy) ReportInputMisMatch(recognizer Parser, e *InputMisMatchException) {
|
||||||
|
msg := "mismatched input " + this.GetTokenErrorDisplay(e.offendingToken) +
|
||||||
|
" expecting " + e.getExpectedTokens().StringVerbose(recognizer.GetLiteralNames(), recognizer.GetSymbolicNames(), false)
|
||||||
|
recognizer.NotifyErrorListeners(msg, e.offendingToken, e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is called by {@link //ReportError} when the exception is a
|
||||||
|
// {@link FailedPredicateException}.
|
||||||
|
//
|
||||||
|
// @see //ReportError
|
||||||
|
//
|
||||||
|
// @param recognizer the parser instance
|
||||||
|
// @param e the recognition exception
|
||||||
|
func (d *DefaultErrorStrategy) ReportFailedPredicate(recognizer Parser, e *FailedPredicateException) {
|
||||||
|
ruleName := recognizer.GetRuleNames()[recognizer.GetParserRuleContext().GetRuleIndex()]
|
||||||
|
msg := "rule " + ruleName + " " + e.message
|
||||||
|
recognizer.NotifyErrorListeners(msg, e.offendingToken, e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This method is called to Report a syntax error which requires the removal
|
||||||
|
// of a token from the input stream. At the time d method is called, the
|
||||||
|
// erroneous symbol is current {@code LT(1)} symbol and has not yet been
|
||||||
|
// removed from the input stream. When d method returns,
|
||||||
|
// {@code recognizer} is in error recovery mode.
|
||||||
|
//
|
||||||
|
// <p>This method is called when {@link //singleTokenDeletion} identifies
|
||||||
|
// single-token deletion as a viable recovery strategy for a mismatched
|
||||||
|
// input error.</p>
|
||||||
|
//
|
||||||
|
// <p>The default implementation simply returns if the handler is already in
|
||||||
|
// error recovery mode. Otherwise, it calls {@link //beginErrorCondition} to
|
||||||
|
// enter error recovery mode, followed by calling
|
||||||
|
// {@link Parser//NotifyErrorListeners}.</p>
|
||||||
|
//
|
||||||
|
// @param recognizer the parser instance
|
||||||
|
func (d *DefaultErrorStrategy) ReportUnwantedToken(recognizer Parser) {
|
||||||
|
if d.InErrorRecoveryMode(recognizer) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.beginErrorCondition(recognizer)
|
||||||
|
t := recognizer.GetCurrentToken()
|
||||||
|
tokenName := d.GetTokenErrorDisplay(t)
|
||||||
|
expecting := d.GetExpectedTokens(recognizer)
|
||||||
|
msg := "extraneous input " + tokenName + " expecting " +
|
||||||
|
expecting.StringVerbose(recognizer.GetLiteralNames(), recognizer.GetSymbolicNames(), false)
|
||||||
|
recognizer.NotifyErrorListeners(msg, t, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This method is called to Report a syntax error which requires the
|
||||||
|
// insertion of a missing token into the input stream. At the time d
|
||||||
|
// method is called, the missing token has not yet been inserted. When d
|
||||||
|
// method returns, {@code recognizer} is in error recovery mode.
|
||||||
|
//
|
||||||
|
// <p>This method is called when {@link //singleTokenInsertion} identifies
|
||||||
|
// single-token insertion as a viable recovery strategy for a mismatched
|
||||||
|
// input error.</p>
|
||||||
|
//
|
||||||
|
// <p>The default implementation simply returns if the handler is already in
|
||||||
|
// error recovery mode. Otherwise, it calls {@link //beginErrorCondition} to
|
||||||
|
// enter error recovery mode, followed by calling
|
||||||
|
// {@link Parser//NotifyErrorListeners}.</p>
|
||||||
|
//
|
||||||
|
// @param recognizer the parser instance
|
||||||
|
func (d *DefaultErrorStrategy) ReportMissingToken(recognizer Parser) {
|
||||||
|
if d.InErrorRecoveryMode(recognizer) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.beginErrorCondition(recognizer)
|
||||||
|
t := recognizer.GetCurrentToken()
|
||||||
|
expecting := d.GetExpectedTokens(recognizer)
|
||||||
|
msg := "missing " + expecting.StringVerbose(recognizer.GetLiteralNames(), recognizer.GetSymbolicNames(), false) +
|
||||||
|
" at " + d.GetTokenErrorDisplay(t)
|
||||||
|
recognizer.NotifyErrorListeners(msg, t, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// <p>The default implementation attempts to recover from the mismatched input
|
||||||
|
// by using single token insertion and deletion as described below. If the
|
||||||
|
// recovery attempt fails, d method panics an
|
||||||
|
// {@link InputMisMatchException}.</p>
|
||||||
|
//
|
||||||
|
// <p><strong>EXTRA TOKEN</strong> (single token deletion)</p>
|
||||||
|
//
|
||||||
|
// <p>{@code LA(1)} is not what we are looking for. If {@code LA(2)} has the
|
||||||
|
// right token, however, then assume {@code LA(1)} is some extra spurious
|
||||||
|
// token and delete it. Then consume and return the next token (which was
|
||||||
|
// the {@code LA(2)} token) as the successful result of the Match operation.</p>
|
||||||
|
//
|
||||||
|
// <p>This recovery strategy is implemented by {@link
|
||||||
|
// //singleTokenDeletion}.</p>
|
||||||
|
//
|
||||||
|
// <p><strong>MISSING TOKEN</strong> (single token insertion)</p>
|
||||||
|
//
|
||||||
|
// <p>If current token (at {@code LA(1)}) is consistent with what could come
|
||||||
|
// after the expected {@code LA(1)} token, then assume the token is missing
|
||||||
|
// and use the parser's {@link TokenFactory} to create it on the fly. The
|
||||||
|
// "insertion" is performed by returning the created token as the successful
|
||||||
|
// result of the Match operation.</p>
|
||||||
|
//
|
||||||
|
// <p>This recovery strategy is implemented by {@link
|
||||||
|
// //singleTokenInsertion}.</p>
|
||||||
|
//
|
||||||
|
// <p><strong>EXAMPLE</strong></p>
|
||||||
|
//
|
||||||
|
// <p>For example, Input {@code i=(3} is clearly missing the {@code ')'}. When
|
||||||
|
// the parser returns from the nested call to {@code expr}, it will have
|
||||||
|
// call chain:</p>
|
||||||
|
//
|
||||||
|
// <pre>
|
||||||
|
// stat &rarr expr &rarr atom
|
||||||
|
// </pre>
|
||||||
|
//
|
||||||
|
// and it will be trying to Match the {@code ')'} at d point in the
|
||||||
|
// derivation:
|
||||||
|
//
|
||||||
|
// <pre>
|
||||||
|
// => ID '=' '(' INT ')' ('+' atom)* ”
|
||||||
|
// ^
|
||||||
|
// </pre>
|
||||||
|
//
|
||||||
|
// The attempt to Match {@code ')'} will fail when it sees {@code ”} and
|
||||||
|
// call {@link //recoverInline}. To recover, it sees that {@code LA(1)==”}
|
||||||
|
// is in the set of tokens that can follow the {@code ')'} token reference
|
||||||
|
// in rule {@code atom}. It can assume that you forgot the {@code ')'}.
|
||||||
|
func (d *DefaultErrorStrategy) RecoverInline(recognizer Parser) Token {
|
||||||
|
// SINGLE TOKEN DELETION
|
||||||
|
MatchedSymbol := d.SingleTokenDeletion(recognizer)
|
||||||
|
if MatchedSymbol != nil {
|
||||||
|
// we have deleted the extra token.
|
||||||
|
// now, move past ttype token as if all were ok
|
||||||
|
recognizer.Consume()
|
||||||
|
return MatchedSymbol
|
||||||
|
}
|
||||||
|
// SINGLE TOKEN INSERTION
|
||||||
|
if d.SingleTokenInsertion(recognizer) {
|
||||||
|
return d.GetMissingSymbol(recognizer)
|
||||||
|
}
|
||||||
|
// even that didn't work must panic the exception
|
||||||
|
panic(NewInputMisMatchException(recognizer))
|
||||||
|
}
|
||||||
|
|
||||||
|
// This method implements the single-token insertion inline error recovery
|
||||||
|
// strategy. It is called by {@link //recoverInline} if the single-token
|
||||||
|
// deletion strategy fails to recover from the mismatched input. If this
|
||||||
|
// method returns {@code true}, {@code recognizer} will be in error recovery
|
||||||
|
// mode.
|
||||||
|
//
|
||||||
|
// <p>This method determines whether or not single-token insertion is viable by
|
||||||
|
// checking if the {@code LA(1)} input symbol could be successfully Matched
|
||||||
|
// if it were instead the {@code LA(2)} symbol. If d method returns
|
||||||
|
// {@code true}, the caller is responsible for creating and inserting a
|
||||||
|
// token with the correct type to produce d behavior.</p>
|
||||||
|
//
|
||||||
|
// @param recognizer the parser instance
|
||||||
|
// @return {@code true} if single-token insertion is a viable recovery
|
||||||
|
// strategy for the current mismatched input, otherwise {@code false}
|
||||||
|
func (d *DefaultErrorStrategy) SingleTokenInsertion(recognizer Parser) bool {
|
||||||
|
currentSymbolType := recognizer.GetTokenStream().LA(1)
|
||||||
|
// if current token is consistent with what could come after current
|
||||||
|
// ATN state, then we know we're missing a token error recovery
|
||||||
|
// is free to conjure up and insert the missing token
|
||||||
|
atn := recognizer.GetInterpreter().atn
|
||||||
|
currentState := atn.states[recognizer.GetState()]
|
||||||
|
next := currentState.GetTransitions()[0].getTarget()
|
||||||
|
expectingAtLL2 := atn.NextTokens(next, recognizer.GetParserRuleContext())
|
||||||
|
if expectingAtLL2.contains(currentSymbolType) {
|
||||||
|
d.ReportMissingToken(recognizer)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// This method implements the single-token deletion inline error recovery
|
||||||
|
// strategy. It is called by {@link //recoverInline} to attempt to recover
|
||||||
|
// from mismatched input. If this method returns nil, the parser and error
|
||||||
|
// handler state will not have changed. If this method returns non-nil,
|
||||||
|
// {@code recognizer} will <em>not</em> be in error recovery mode since the
|
||||||
|
// returned token was a successful Match.
|
||||||
|
//
|
||||||
|
// <p>If the single-token deletion is successful, d method calls
|
||||||
|
// {@link //ReportUnwantedToken} to Report the error, followed by
|
||||||
|
// {@link Parser//consume} to actually "delete" the extraneous token. Then,
|
||||||
|
// before returning {@link //ReportMatch} is called to signal a successful
|
||||||
|
// Match.</p>
|
||||||
|
//
|
||||||
|
// @param recognizer the parser instance
|
||||||
|
// @return the successfully Matched {@link Token} instance if single-token
|
||||||
|
// deletion successfully recovers from the mismatched input, otherwise
|
||||||
|
// {@code nil}
|
||||||
|
func (d *DefaultErrorStrategy) SingleTokenDeletion(recognizer Parser) Token {
|
||||||
|
NextTokenType := recognizer.GetTokenStream().LA(2)
|
||||||
|
expecting := d.GetExpectedTokens(recognizer)
|
||||||
|
if expecting.contains(NextTokenType) {
|
||||||
|
d.ReportUnwantedToken(recognizer)
|
||||||
|
// print("recoverFromMisMatchedToken deleting " \
|
||||||
|
// + str(recognizer.GetTokenStream().LT(1)) \
|
||||||
|
// + " since " + str(recognizer.GetTokenStream().LT(2)) \
|
||||||
|
// + " is what we want", file=sys.stderr)
|
||||||
|
recognizer.Consume() // simply delete extra token
|
||||||
|
// we want to return the token we're actually Matching
|
||||||
|
MatchedSymbol := recognizer.GetCurrentToken()
|
||||||
|
d.ReportMatch(recognizer) // we know current token is correct
|
||||||
|
return MatchedSymbol
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conjure up a missing token during error recovery.
|
||||||
|
//
|
||||||
|
// The recognizer attempts to recover from single missing
|
||||||
|
// symbols. But, actions might refer to that missing symbol.
|
||||||
|
// For example, x=ID {f($x)}. The action clearly assumes
|
||||||
|
// that there has been an identifier Matched previously and that
|
||||||
|
// $x points at that token. If that token is missing, but
|
||||||
|
// the next token in the stream is what we want we assume that
|
||||||
|
// d token is missing and we keep going. Because we
|
||||||
|
// have to return some token to replace the missing token,
|
||||||
|
// we have to conjure one up. This method gives the user control
|
||||||
|
// over the tokens returned for missing tokens. Mostly,
|
||||||
|
// you will want to create something special for identifier
|
||||||
|
// tokens. For literals such as '{' and ',', the default
|
||||||
|
// action in the parser or tree parser works. It simply creates
|
||||||
|
// a CommonToken of the appropriate type. The text will be the token.
|
||||||
|
// If you change what tokens must be created by the lexer,
|
||||||
|
// override d method to create the appropriate tokens.
|
||||||
|
func (d *DefaultErrorStrategy) GetMissingSymbol(recognizer Parser) Token {
|
||||||
|
currentSymbol := recognizer.GetCurrentToken()
|
||||||
|
expecting := d.GetExpectedTokens(recognizer)
|
||||||
|
expectedTokenType := expecting.first()
|
||||||
|
var tokenText string
|
||||||
|
|
||||||
|
if expectedTokenType == TokenEOF {
|
||||||
|
tokenText = "<missing EOF>"
|
||||||
|
} else {
|
||||||
|
ln := recognizer.GetLiteralNames()
|
||||||
|
if expectedTokenType > 0 && expectedTokenType < len(ln) {
|
||||||
|
tokenText = "<missing " + recognizer.GetLiteralNames()[expectedTokenType] + ">"
|
||||||
|
} else {
|
||||||
|
tokenText = "<missing undefined>" // TODO matches the JS impl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
current := currentSymbol
|
||||||
|
lookback := recognizer.GetTokenStream().LT(-1)
|
||||||
|
if current.GetTokenType() == TokenEOF && lookback != nil {
|
||||||
|
current = lookback
|
||||||
|
}
|
||||||
|
|
||||||
|
tf := recognizer.GetTokenFactory()
|
||||||
|
|
||||||
|
return tf.Create(current.GetSource(), expectedTokenType, tokenText, TokenDefaultChannel, -1, -1, current.GetLine(), current.GetColumn())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DefaultErrorStrategy) GetExpectedTokens(recognizer Parser) *IntervalSet {
|
||||||
|
return recognizer.GetExpectedTokens()
|
||||||
|
}
|
||||||
|
|
||||||
|
// How should a token be displayed in an error message? The default
|
||||||
|
// is to display just the text, but during development you might
|
||||||
|
// want to have a lot of information spit out. Override in that case
|
||||||
|
// to use t.String() (which, for CommonToken, dumps everything about
|
||||||
|
// the token). This is better than forcing you to override a method in
|
||||||
|
// your token objects because you don't have to go modify your lexer
|
||||||
|
// so that it creates a NewJava type.
|
||||||
|
func (d *DefaultErrorStrategy) GetTokenErrorDisplay(t Token) string {
|
||||||
|
if t == nil {
|
||||||
|
return "<no token>"
|
||||||
|
}
|
||||||
|
s := t.GetText()
|
||||||
|
if s == "" {
|
||||||
|
if t.GetTokenType() == TokenEOF {
|
||||||
|
s = "<EOF>"
|
||||||
|
} else {
|
||||||
|
s = "<" + strconv.Itoa(t.GetTokenType()) + ">"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return d.escapeWSAndQuote(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DefaultErrorStrategy) escapeWSAndQuote(s string) string {
|
||||||
|
s = strings.Replace(s, "\t", "\\t", -1)
|
||||||
|
s = strings.Replace(s, "\n", "\\n", -1)
|
||||||
|
s = strings.Replace(s, "\r", "\\r", -1)
|
||||||
|
return "'" + s + "'"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute the error recovery set for the current rule. During
|
||||||
|
// rule invocation, the parser pushes the set of tokens that can
|
||||||
|
// follow that rule reference on the stack d amounts to
|
||||||
|
// computing FIRST of what follows the rule reference in the
|
||||||
|
// enclosing rule. See LinearApproximator.FIRST().
|
||||||
|
// This local follow set only includes tokens
|
||||||
|
// from within the rule i.e., the FIRST computation done by
|
||||||
|
// ANTLR stops at the end of a rule.
|
||||||
|
//
|
||||||
|
// # EXAMPLE
|
||||||
|
//
|
||||||
|
// When you find a "no viable alt exception", the input is not
|
||||||
|
// consistent with any of the alternatives for rule r. The best
|
||||||
|
// thing to do is to consume tokens until you see something that
|
||||||
|
// can legally follow a call to r//or* any rule that called r.
|
||||||
|
// You don't want the exact set of viable next tokens because the
|
||||||
|
// input might just be missing a token--you might consume the
|
||||||
|
// rest of the input looking for one of the missing tokens.
|
||||||
|
//
|
||||||
|
// Consider grammar:
|
||||||
|
//
|
||||||
|
// a : '[' b ']'
|
||||||
|
// | '(' b ')'
|
||||||
|
//
|
||||||
|
// b : c '^' INT
|
||||||
|
// c : ID
|
||||||
|
// | INT
|
||||||
|
//
|
||||||
|
// At each rule invocation, the set of tokens that could follow
|
||||||
|
// that rule is pushed on a stack. Here are the various
|
||||||
|
// context-sensitive follow sets:
|
||||||
|
//
|
||||||
|
// FOLLOW(b1_in_a) = FIRST(']') = ']'
|
||||||
|
// FOLLOW(b2_in_a) = FIRST(')') = ')'
|
||||||
|
// FOLLOW(c_in_b) = FIRST('^') = '^'
|
||||||
|
//
|
||||||
|
// Upon erroneous input "[]", the call chain is
|
||||||
|
//
|
||||||
|
// a -> b -> c
|
||||||
|
//
|
||||||
|
// and, hence, the follow context stack is:
|
||||||
|
//
|
||||||
|
// depth follow set start of rule execution
|
||||||
|
// 0 <EOF> a (from main())
|
||||||
|
// 1 ']' b
|
||||||
|
// 2 '^' c
|
||||||
|
//
|
||||||
|
// Notice that ')' is not included, because b would have to have
|
||||||
|
// been called from a different context in rule a for ')' to be
|
||||||
|
// included.
|
||||||
|
//
|
||||||
|
// For error recovery, we cannot consider FOLLOW(c)
|
||||||
|
// (context-sensitive or otherwise). We need the combined set of
|
||||||
|
// all context-sensitive FOLLOW sets--the set of all tokens that
|
||||||
|
// could follow any reference in the call chain. We need to
|
||||||
|
// reSync to one of those tokens. Note that FOLLOW(c)='^' and if
|
||||||
|
// we reSync'd to that token, we'd consume until EOF. We need to
|
||||||
|
// Sync to context-sensitive FOLLOWs for a, b, and c: {']','^'}.
|
||||||
|
// In this case, for input "[]", LA(1) is ']' and in the set, so we would
|
||||||
|
// not consume anything. After printing an error, rule c would
|
||||||
|
// return normally. Rule b would not find the required '^' though.
|
||||||
|
// At this point, it gets a mismatched token error and panics an
|
||||||
|
// exception (since LA(1) is not in the viable following token
|
||||||
|
// set). The rule exception handler tries to recover, but finds
|
||||||
|
// the same recovery set and doesn't consume anything. Rule b
|
||||||
|
// exits normally returning to rule a. Now it finds the ']' (and
|
||||||
|
// with the successful Match exits errorRecovery mode).
|
||||||
|
//
|
||||||
|
// So, you can see that the parser walks up the call chain looking
|
||||||
|
// for the token that was a member of the recovery set.
|
||||||
|
//
|
||||||
|
// Errors are not generated in errorRecovery mode.
|
||||||
|
//
|
||||||
|
// ANTLR's error recovery mechanism is based upon original ideas:
|
||||||
|
//
|
||||||
|
// "Algorithms + Data Structures = Programs" by Niklaus Wirth
|
||||||
|
//
|
||||||
|
// and
|
||||||
|
//
|
||||||
|
// "A note on error recovery in recursive descent parsers":
|
||||||
|
// http://portal.acm.org/citation.cfm?id=947902.947905
|
||||||
|
//
|
||||||
|
// Later, Josef Grosch had some good ideas:
|
||||||
|
//
|
||||||
|
// "Efficient and Comfortable Error Recovery in Recursive Descent
|
||||||
|
// Parsers":
|
||||||
|
// ftp://www.cocolab.com/products/cocktail/doca4.ps/ell.ps.zip
|
||||||
|
//
|
||||||
|
// Like Grosch I implement context-sensitive FOLLOW sets that are combined
|
||||||
|
// at run-time upon error to avoid overhead during parsing.
|
||||||
|
func (d *DefaultErrorStrategy) getErrorRecoverySet(recognizer Parser) *IntervalSet {
|
||||||
|
atn := recognizer.GetInterpreter().atn
|
||||||
|
ctx := recognizer.GetParserRuleContext()
|
||||||
|
recoverSet := NewIntervalSet()
|
||||||
|
for ctx != nil && ctx.GetInvokingState() >= 0 {
|
||||||
|
// compute what follows who invoked us
|
||||||
|
invokingState := atn.states[ctx.GetInvokingState()]
|
||||||
|
rt := invokingState.GetTransitions()[0]
|
||||||
|
follow := atn.NextTokens(rt.(*RuleTransition).followState, nil)
|
||||||
|
recoverSet.addSet(follow)
|
||||||
|
ctx = ctx.GetParent().(ParserRuleContext)
|
||||||
|
}
|
||||||
|
recoverSet.removeOne(TokenEpsilon)
|
||||||
|
return recoverSet
|
||||||
|
}
|
||||||
|
|
||||||
|
// Consume tokens until one Matches the given token set.//
|
||||||
|
func (d *DefaultErrorStrategy) consumeUntil(recognizer Parser, set *IntervalSet) {
|
||||||
|
ttype := recognizer.GetTokenStream().LA(1)
|
||||||
|
for ttype != TokenEOF && !set.contains(ttype) {
|
||||||
|
recognizer.Consume()
|
||||||
|
ttype = recognizer.GetTokenStream().LA(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// This implementation of {@link ANTLRErrorStrategy} responds to syntax errors
|
||||||
|
// by immediately canceling the parse operation with a
|
||||||
|
// {@link ParseCancellationException}. The implementation ensures that the
|
||||||
|
// {@link ParserRuleContext//exception} field is set for all parse tree nodes
|
||||||
|
// that were not completed prior to encountering the error.
|
||||||
|
//
|
||||||
|
// <p>
|
||||||
|
// This error strategy is useful in the following scenarios.</p>
|
||||||
|
//
|
||||||
|
// <ul>
|
||||||
|
// <li><strong>Two-stage parsing:</strong> This error strategy allows the first
|
||||||
|
// stage of two-stage parsing to immediately terminate if an error is
|
||||||
|
// encountered, and immediately fall back to the second stage. In addition to
|
||||||
|
// avoiding wasted work by attempting to recover from errors here, the empty
|
||||||
|
// implementation of {@link BailErrorStrategy//Sync} improves the performance of
|
||||||
|
// the first stage.</li>
|
||||||
|
// <li><strong>Silent validation:</strong> When syntax errors are not being
|
||||||
|
// Reported or logged, and the parse result is simply ignored if errors occur,
|
||||||
|
// the {@link BailErrorStrategy} avoids wasting work on recovering from errors
|
||||||
|
// when the result will be ignored either way.</li>
|
||||||
|
// </ul>
|
||||||
|
//
|
||||||
|
// <p>
|
||||||
|
// {@code myparser.setErrorHandler(NewBailErrorStrategy())}</p>
|
||||||
|
//
|
||||||
|
// @see Parser//setErrorHandler(ANTLRErrorStrategy)
|
||||||
|
|
||||||
|
type BailErrorStrategy struct {
|
||||||
|
*DefaultErrorStrategy
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ ErrorStrategy = &BailErrorStrategy{}
|
||||||
|
|
||||||
|
func NewBailErrorStrategy() *BailErrorStrategy {
|
||||||
|
|
||||||
|
b := new(BailErrorStrategy)
|
||||||
|
|
||||||
|
b.DefaultErrorStrategy = NewDefaultErrorStrategy()
|
||||||
|
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// Instead of recovering from exception {@code e}, re-panic it wrapped
|
||||||
|
// in a {@link ParseCancellationException} so it is not caught by the
|
||||||
|
// rule func catches. Use {@link Exception//getCause()} to get the
|
||||||
|
// original {@link RecognitionException}.
|
||||||
|
func (b *BailErrorStrategy) Recover(recognizer Parser, e RecognitionException) {
|
||||||
|
context := recognizer.GetParserRuleContext()
|
||||||
|
for context != nil {
|
||||||
|
context.SetException(e)
|
||||||
|
if parent, ok := context.GetParent().(ParserRuleContext); ok {
|
||||||
|
context = parent
|
||||||
|
} else {
|
||||||
|
context = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic(NewParseCancellationException()) // TODO we don't emit e properly
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure we don't attempt to recover inline if the parser
|
||||||
|
// successfully recovers, it won't panic an exception.
|
||||||
|
func (b *BailErrorStrategy) RecoverInline(recognizer Parser) Token {
|
||||||
|
b.Recover(recognizer, NewInputMisMatchException(recognizer))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure we don't attempt to recover from problems in subrules.//
|
||||||
|
func (b *BailErrorStrategy) Sync(recognizer Parser) {
|
||||||
|
// pass
|
||||||
|
}
|
||||||
238
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/errors.go
generated
vendored
Normal file
238
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/errors.go
generated
vendored
Normal file
@@ -0,0 +1,238 @@
|
|||||||
|
// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
||||||
|
// Use of this file is governed by the BSD 3-clause license that
|
||||||
|
// can be found in the LICENSE.txt file in the project root.
|
||||||
|
|
||||||
|
package antlr
|
||||||
|
|
||||||
|
// The root of the ANTLR exception hierarchy. In general, ANTLR tracks just
|
||||||
|
// 3 kinds of errors: prediction errors, failed predicate errors, and
|
||||||
|
// mismatched input errors. In each case, the parser knows where it is
|
||||||
|
// in the input, where it is in the ATN, the rule invocation stack,
|
||||||
|
// and what kind of problem occurred.
|
||||||
|
|
||||||
|
type RecognitionException interface {
|
||||||
|
GetOffendingToken() Token
|
||||||
|
GetMessage() string
|
||||||
|
GetInputStream() IntStream
|
||||||
|
}
|
||||||
|
|
||||||
|
type BaseRecognitionException struct {
|
||||||
|
message string
|
||||||
|
recognizer Recognizer
|
||||||
|
offendingToken Token
|
||||||
|
offendingState int
|
||||||
|
ctx RuleContext
|
||||||
|
input IntStream
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBaseRecognitionException(message string, recognizer Recognizer, input IntStream, ctx RuleContext) *BaseRecognitionException {
|
||||||
|
|
||||||
|
// todo
|
||||||
|
// Error.call(this)
|
||||||
|
//
|
||||||
|
// if (!!Error.captureStackTrace) {
|
||||||
|
// Error.captureStackTrace(this, RecognitionException)
|
||||||
|
// } else {
|
||||||
|
// stack := NewError().stack
|
||||||
|
// }
|
||||||
|
// TODO may be able to use - "runtime" func Stack(buf []byte, all bool) int
|
||||||
|
|
||||||
|
t := new(BaseRecognitionException)
|
||||||
|
|
||||||
|
t.message = message
|
||||||
|
t.recognizer = recognizer
|
||||||
|
t.input = input
|
||||||
|
t.ctx = ctx
|
||||||
|
// The current {@link Token} when an error occurred. Since not all streams
|
||||||
|
// support accessing symbols by index, we have to track the {@link Token}
|
||||||
|
// instance itself.
|
||||||
|
t.offendingToken = nil
|
||||||
|
// Get the ATN state number the parser was in at the time the error
|
||||||
|
// occurred. For {@link NoViableAltException} and
|
||||||
|
// {@link LexerNoViableAltException} exceptions, this is the
|
||||||
|
// {@link DecisionState} number. For others, it is the state whose outgoing
|
||||||
|
// edge we couldn't Match.
|
||||||
|
t.offendingState = -1
|
||||||
|
if t.recognizer != nil {
|
||||||
|
t.offendingState = t.recognizer.GetState()
|
||||||
|
}
|
||||||
|
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseRecognitionException) GetMessage() string {
|
||||||
|
return b.message
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseRecognitionException) GetOffendingToken() Token {
|
||||||
|
return b.offendingToken
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseRecognitionException) GetInputStream() IntStream {
|
||||||
|
return b.input
|
||||||
|
}
|
||||||
|
|
||||||
|
// <p>If the state number is not known, b method returns -1.</p>
|
||||||
|
|
||||||
|
// Gets the set of input symbols which could potentially follow the
|
||||||
|
// previously Matched symbol at the time b exception was panicn.
|
||||||
|
//
|
||||||
|
// <p>If the set of expected tokens is not known and could not be computed,
|
||||||
|
// b method returns {@code nil}.</p>
|
||||||
|
//
|
||||||
|
// @return The set of token types that could potentially follow the current
|
||||||
|
// state in the ATN, or {@code nil} if the information is not available.
|
||||||
|
// /
|
||||||
|
func (b *BaseRecognitionException) getExpectedTokens() *IntervalSet {
|
||||||
|
if b.recognizer != nil {
|
||||||
|
return b.recognizer.GetATN().getExpectedTokens(b.offendingState, b.ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseRecognitionException) String() string {
|
||||||
|
return b.message
|
||||||
|
}
|
||||||
|
|
||||||
|
type LexerNoViableAltException struct {
|
||||||
|
*BaseRecognitionException
|
||||||
|
|
||||||
|
startIndex int
|
||||||
|
deadEndConfigs ATNConfigSet
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLexerNoViableAltException(lexer Lexer, input CharStream, startIndex int, deadEndConfigs ATNConfigSet) *LexerNoViableAltException {
|
||||||
|
|
||||||
|
l := new(LexerNoViableAltException)
|
||||||
|
|
||||||
|
l.BaseRecognitionException = NewBaseRecognitionException("", lexer, input, nil)
|
||||||
|
|
||||||
|
l.startIndex = startIndex
|
||||||
|
l.deadEndConfigs = deadEndConfigs
|
||||||
|
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LexerNoViableAltException) String() string {
|
||||||
|
symbol := ""
|
||||||
|
if l.startIndex >= 0 && l.startIndex < l.input.Size() {
|
||||||
|
symbol = l.input.(CharStream).GetTextFromInterval(NewInterval(l.startIndex, l.startIndex))
|
||||||
|
}
|
||||||
|
return "LexerNoViableAltException" + symbol
|
||||||
|
}
|
||||||
|
|
||||||
|
type NoViableAltException struct {
|
||||||
|
*BaseRecognitionException
|
||||||
|
|
||||||
|
startToken Token
|
||||||
|
offendingToken Token
|
||||||
|
ctx ParserRuleContext
|
||||||
|
deadEndConfigs ATNConfigSet
|
||||||
|
}
|
||||||
|
|
||||||
|
// Indicates that the parser could not decide which of two or more paths
|
||||||
|
// to take based upon the remaining input. It tracks the starting token
|
||||||
|
// of the offending input and also knows where the parser was
|
||||||
|
// in the various paths when the error. Reported by ReportNoViableAlternative()
|
||||||
|
func NewNoViableAltException(recognizer Parser, input TokenStream, startToken Token, offendingToken Token, deadEndConfigs ATNConfigSet, ctx ParserRuleContext) *NoViableAltException {
|
||||||
|
|
||||||
|
if ctx == nil {
|
||||||
|
ctx = recognizer.GetParserRuleContext()
|
||||||
|
}
|
||||||
|
|
||||||
|
if offendingToken == nil {
|
||||||
|
offendingToken = recognizer.GetCurrentToken()
|
||||||
|
}
|
||||||
|
|
||||||
|
if startToken == nil {
|
||||||
|
startToken = recognizer.GetCurrentToken()
|
||||||
|
}
|
||||||
|
|
||||||
|
if input == nil {
|
||||||
|
input = recognizer.GetInputStream().(TokenStream)
|
||||||
|
}
|
||||||
|
|
||||||
|
n := new(NoViableAltException)
|
||||||
|
n.BaseRecognitionException = NewBaseRecognitionException("", recognizer, input, ctx)
|
||||||
|
|
||||||
|
// Which configurations did we try at input.Index() that couldn't Match
|
||||||
|
// input.LT(1)?//
|
||||||
|
n.deadEndConfigs = deadEndConfigs
|
||||||
|
// The token object at the start index the input stream might
|
||||||
|
// not be buffering tokens so get a reference to it. (At the
|
||||||
|
// time the error occurred, of course the stream needs to keep a
|
||||||
|
// buffer all of the tokens but later we might not have access to those.)
|
||||||
|
n.startToken = startToken
|
||||||
|
n.offendingToken = offendingToken
|
||||||
|
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
type InputMisMatchException struct {
|
||||||
|
*BaseRecognitionException
|
||||||
|
}
|
||||||
|
|
||||||
|
// This signifies any kind of mismatched input exceptions such as
|
||||||
|
// when the current input does not Match the expected token.
|
||||||
|
func NewInputMisMatchException(recognizer Parser) *InputMisMatchException {
|
||||||
|
|
||||||
|
i := new(InputMisMatchException)
|
||||||
|
i.BaseRecognitionException = NewBaseRecognitionException("", recognizer, recognizer.GetInputStream(), recognizer.GetParserRuleContext())
|
||||||
|
|
||||||
|
i.offendingToken = recognizer.GetCurrentToken()
|
||||||
|
|
||||||
|
return i
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// A semantic predicate failed during validation. Validation of predicates
|
||||||
|
// occurs when normally parsing the alternative just like Matching a token.
|
||||||
|
// Disambiguating predicate evaluation occurs when we test a predicate during
|
||||||
|
// prediction.
|
||||||
|
|
||||||
|
type FailedPredicateException struct {
|
||||||
|
*BaseRecognitionException
|
||||||
|
|
||||||
|
ruleIndex int
|
||||||
|
predicateIndex int
|
||||||
|
predicate string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFailedPredicateException(recognizer Parser, predicate string, message string) *FailedPredicateException {
|
||||||
|
|
||||||
|
f := new(FailedPredicateException)
|
||||||
|
|
||||||
|
f.BaseRecognitionException = NewBaseRecognitionException(f.formatMessage(predicate, message), recognizer, recognizer.GetInputStream(), recognizer.GetParserRuleContext())
|
||||||
|
|
||||||
|
s := recognizer.GetInterpreter().atn.states[recognizer.GetState()]
|
||||||
|
trans := s.GetTransitions()[0]
|
||||||
|
if trans2, ok := trans.(*PredicateTransition); ok {
|
||||||
|
f.ruleIndex = trans2.ruleIndex
|
||||||
|
f.predicateIndex = trans2.predIndex
|
||||||
|
} else {
|
||||||
|
f.ruleIndex = 0
|
||||||
|
f.predicateIndex = 0
|
||||||
|
}
|
||||||
|
f.predicate = predicate
|
||||||
|
f.offendingToken = recognizer.GetCurrentToken()
|
||||||
|
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FailedPredicateException) formatMessage(predicate, message string) string {
|
||||||
|
if message != "" {
|
||||||
|
return message
|
||||||
|
}
|
||||||
|
|
||||||
|
return "failed predicate: {" + predicate + "}?"
|
||||||
|
}
|
||||||
|
|
||||||
|
type ParseCancellationException struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewParseCancellationException() *ParseCancellationException {
|
||||||
|
// Error.call(this)
|
||||||
|
// Error.captureStackTrace(this, ParseCancellationException)
|
||||||
|
return new(ParseCancellationException)
|
||||||
|
}
|
||||||
49
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/file_stream.go
generated
vendored
Normal file
49
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/file_stream.go
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
||||||
|
// Use of this file is governed by the BSD 3-clause license that
|
||||||
|
// can be found in the LICENSE.txt file in the project root.
|
||||||
|
|
||||||
|
package antlr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This is an InputStream that is loaded from a file all at once
|
||||||
|
// when you construct the object.
|
||||||
|
|
||||||
|
type FileStream struct {
|
||||||
|
*InputStream
|
||||||
|
|
||||||
|
filename string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFileStream(fileName string) (*FileStream, error) {
|
||||||
|
|
||||||
|
buf := bytes.NewBuffer(nil)
|
||||||
|
|
||||||
|
f, err := os.Open(fileName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
_, err = io.Copy(buf, f)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
fs := new(FileStream)
|
||||||
|
|
||||||
|
fs.filename = fileName
|
||||||
|
s := string(buf.Bytes())
|
||||||
|
|
||||||
|
fs.InputStream = NewInputStream(s)
|
||||||
|
|
||||||
|
return fs, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FileStream) GetSourceName() string {
|
||||||
|
return f.filename
|
||||||
|
}
|
||||||
113
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/input_stream.go
generated
vendored
Normal file
113
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/input_stream.go
generated
vendored
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
||||||
|
// Use of this file is governed by the BSD 3-clause license that
|
||||||
|
// can be found in the LICENSE.txt file in the project root.
|
||||||
|
|
||||||
|
package antlr
|
||||||
|
|
||||||
|
type InputStream struct {
|
||||||
|
name string
|
||||||
|
index int
|
||||||
|
data []rune
|
||||||
|
size int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewInputStream(data string) *InputStream {
|
||||||
|
|
||||||
|
is := new(InputStream)
|
||||||
|
|
||||||
|
is.name = "<empty>"
|
||||||
|
is.index = 0
|
||||||
|
is.data = []rune(data)
|
||||||
|
is.size = len(is.data) // number of runes
|
||||||
|
|
||||||
|
return is
|
||||||
|
}
|
||||||
|
|
||||||
|
func (is *InputStream) reset() {
|
||||||
|
is.index = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (is *InputStream) Consume() {
|
||||||
|
if is.index >= is.size {
|
||||||
|
// assert is.LA(1) == TokenEOF
|
||||||
|
panic("cannot consume EOF")
|
||||||
|
}
|
||||||
|
is.index++
|
||||||
|
}
|
||||||
|
|
||||||
|
func (is *InputStream) LA(offset int) int {
|
||||||
|
|
||||||
|
if offset == 0 {
|
||||||
|
return 0 // nil
|
||||||
|
}
|
||||||
|
if offset < 0 {
|
||||||
|
offset++ // e.g., translate LA(-1) to use offset=0
|
||||||
|
}
|
||||||
|
pos := is.index + offset - 1
|
||||||
|
|
||||||
|
if pos < 0 || pos >= is.size { // invalid
|
||||||
|
return TokenEOF
|
||||||
|
}
|
||||||
|
|
||||||
|
return int(is.data[pos])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (is *InputStream) LT(offset int) int {
|
||||||
|
return is.LA(offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (is *InputStream) Index() int {
|
||||||
|
return is.index
|
||||||
|
}
|
||||||
|
|
||||||
|
func (is *InputStream) Size() int {
|
||||||
|
return is.size
|
||||||
|
}
|
||||||
|
|
||||||
|
// mark/release do nothing we have entire buffer
|
||||||
|
func (is *InputStream) Mark() int {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (is *InputStream) Release(marker int) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (is *InputStream) Seek(index int) {
|
||||||
|
if index <= is.index {
|
||||||
|
is.index = index // just jump don't update stream state (line,...)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// seek forward
|
||||||
|
is.index = intMin(index, is.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (is *InputStream) GetText(start int, stop int) string {
|
||||||
|
if stop >= is.size {
|
||||||
|
stop = is.size - 1
|
||||||
|
}
|
||||||
|
if start >= is.size {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(is.data[start : stop+1])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (is *InputStream) GetTextFromTokens(start, stop Token) string {
|
||||||
|
if start != nil && stop != nil {
|
||||||
|
return is.GetTextFromInterval(NewInterval(start.GetTokenIndex(), stop.GetTokenIndex()))
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (is *InputStream) GetTextFromInterval(i *Interval) string {
|
||||||
|
return is.GetText(i.Start, i.Stop)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*InputStream) GetSourceName() string {
|
||||||
|
return "Obtained from string"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (is *InputStream) String() string {
|
||||||
|
return string(is.data)
|
||||||
|
}
|
||||||
16
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/int_stream.go
generated
vendored
Normal file
16
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/int_stream.go
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
||||||
|
// Use of this file is governed by the BSD 3-clause license that
|
||||||
|
// can be found in the LICENSE.txt file in the project root.
|
||||||
|
|
||||||
|
package antlr
|
||||||
|
|
||||||
|
type IntStream interface {
|
||||||
|
Consume()
|
||||||
|
LA(int) int
|
||||||
|
Mark() int
|
||||||
|
Release(marker int)
|
||||||
|
Index() int
|
||||||
|
Seek(index int)
|
||||||
|
Size() int
|
||||||
|
GetSourceName() string
|
||||||
|
}
|
||||||
312
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/interval_set.go
generated
vendored
Normal file
312
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/interval_set.go
generated
vendored
Normal file
@@ -0,0 +1,312 @@
|
|||||||
|
// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
||||||
|
// Use of this file is governed by the BSD 3-clause license that
|
||||||
|
// can be found in the LICENSE.txt file in the project root.
|
||||||
|
|
||||||
|
package antlr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Interval struct {
|
||||||
|
Start int
|
||||||
|
Stop int
|
||||||
|
}
|
||||||
|
|
||||||
|
/* stop is not included! */
|
||||||
|
func NewInterval(start, stop int) *Interval {
|
||||||
|
i := new(Interval)
|
||||||
|
|
||||||
|
i.Start = start
|
||||||
|
i.Stop = stop
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Interval) Contains(item int) bool {
|
||||||
|
return item >= i.Start && item < i.Stop
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Interval) String() string {
|
||||||
|
if i.Start == i.Stop-1 {
|
||||||
|
return strconv.Itoa(i.Start)
|
||||||
|
}
|
||||||
|
|
||||||
|
return strconv.Itoa(i.Start) + ".." + strconv.Itoa(i.Stop-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Interval) length() int {
|
||||||
|
return i.Stop - i.Start
|
||||||
|
}
|
||||||
|
|
||||||
|
type IntervalSet struct {
|
||||||
|
intervals []*Interval
|
||||||
|
readOnly bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewIntervalSet() *IntervalSet {
|
||||||
|
|
||||||
|
i := new(IntervalSet)
|
||||||
|
|
||||||
|
i.intervals = nil
|
||||||
|
i.readOnly = false
|
||||||
|
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *IntervalSet) first() int {
|
||||||
|
if len(i.intervals) == 0 {
|
||||||
|
return TokenInvalidType
|
||||||
|
}
|
||||||
|
|
||||||
|
return i.intervals[0].Start
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *IntervalSet) addOne(v int) {
|
||||||
|
i.addInterval(NewInterval(v, v+1))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *IntervalSet) addRange(l, h int) {
|
||||||
|
i.addInterval(NewInterval(l, h+1))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *IntervalSet) addInterval(v *Interval) {
|
||||||
|
if i.intervals == nil {
|
||||||
|
i.intervals = make([]*Interval, 0)
|
||||||
|
i.intervals = append(i.intervals, v)
|
||||||
|
} else {
|
||||||
|
// find insert pos
|
||||||
|
for k, interval := range i.intervals {
|
||||||
|
// distinct range -> insert
|
||||||
|
if v.Stop < interval.Start {
|
||||||
|
i.intervals = append(i.intervals[0:k], append([]*Interval{v}, i.intervals[k:]...)...)
|
||||||
|
return
|
||||||
|
} else if v.Stop == interval.Start {
|
||||||
|
i.intervals[k].Start = v.Start
|
||||||
|
return
|
||||||
|
} else if v.Start <= interval.Stop {
|
||||||
|
i.intervals[k] = NewInterval(intMin(interval.Start, v.Start), intMax(interval.Stop, v.Stop))
|
||||||
|
|
||||||
|
// if not applying to end, merge potential overlaps
|
||||||
|
if k < len(i.intervals)-1 {
|
||||||
|
l := i.intervals[k]
|
||||||
|
r := i.intervals[k+1]
|
||||||
|
// if r contained in l
|
||||||
|
if l.Stop >= r.Stop {
|
||||||
|
i.intervals = append(i.intervals[0:k+1], i.intervals[k+2:]...)
|
||||||
|
} else if l.Stop >= r.Start { // partial overlap
|
||||||
|
i.intervals[k] = NewInterval(l.Start, r.Stop)
|
||||||
|
i.intervals = append(i.intervals[0:k+1], i.intervals[k+2:]...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// greater than any exiting
|
||||||
|
i.intervals = append(i.intervals, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *IntervalSet) addSet(other *IntervalSet) *IntervalSet {
|
||||||
|
if other.intervals != nil {
|
||||||
|
for k := 0; k < len(other.intervals); k++ {
|
||||||
|
i2 := other.intervals[k]
|
||||||
|
i.addInterval(NewInterval(i2.Start, i2.Stop))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *IntervalSet) complement(start int, stop int) *IntervalSet {
|
||||||
|
result := NewIntervalSet()
|
||||||
|
result.addInterval(NewInterval(start, stop+1))
|
||||||
|
for j := 0; j < len(i.intervals); j++ {
|
||||||
|
result.removeRange(i.intervals[j])
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *IntervalSet) contains(item int) bool {
|
||||||
|
if i.intervals == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for k := 0; k < len(i.intervals); k++ {
|
||||||
|
if i.intervals[k].Contains(item) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *IntervalSet) length() int {
|
||||||
|
len := 0
|
||||||
|
|
||||||
|
for _, v := range i.intervals {
|
||||||
|
len += v.length()
|
||||||
|
}
|
||||||
|
|
||||||
|
return len
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *IntervalSet) removeRange(v *Interval) {
|
||||||
|
if v.Start == v.Stop-1 {
|
||||||
|
i.removeOne(v.Start)
|
||||||
|
} else if i.intervals != nil {
|
||||||
|
k := 0
|
||||||
|
for n := 0; n < len(i.intervals); n++ {
|
||||||
|
ni := i.intervals[k]
|
||||||
|
// intervals are ordered
|
||||||
|
if v.Stop <= ni.Start {
|
||||||
|
return
|
||||||
|
} else if v.Start > ni.Start && v.Stop < ni.Stop {
|
||||||
|
i.intervals[k] = NewInterval(ni.Start, v.Start)
|
||||||
|
x := NewInterval(v.Stop, ni.Stop)
|
||||||
|
// i.intervals.splice(k, 0, x)
|
||||||
|
i.intervals = append(i.intervals[0:k], append([]*Interval{x}, i.intervals[k:]...)...)
|
||||||
|
return
|
||||||
|
} else if v.Start <= ni.Start && v.Stop >= ni.Stop {
|
||||||
|
// i.intervals.splice(k, 1)
|
||||||
|
i.intervals = append(i.intervals[0:k], i.intervals[k+1:]...)
|
||||||
|
k = k - 1 // need another pass
|
||||||
|
} else if v.Start < ni.Stop {
|
||||||
|
i.intervals[k] = NewInterval(ni.Start, v.Start)
|
||||||
|
} else if v.Stop < ni.Stop {
|
||||||
|
i.intervals[k] = NewInterval(v.Stop, ni.Stop)
|
||||||
|
}
|
||||||
|
k++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *IntervalSet) removeOne(v int) {
|
||||||
|
if i.intervals != nil {
|
||||||
|
for k := 0; k < len(i.intervals); k++ {
|
||||||
|
ki := i.intervals[k]
|
||||||
|
// intervals i ordered
|
||||||
|
if v < ki.Start {
|
||||||
|
return
|
||||||
|
} else if v == ki.Start && v == ki.Stop-1 {
|
||||||
|
// i.intervals.splice(k, 1)
|
||||||
|
i.intervals = append(i.intervals[0:k], i.intervals[k+1:]...)
|
||||||
|
return
|
||||||
|
} else if v == ki.Start {
|
||||||
|
i.intervals[k] = NewInterval(ki.Start+1, ki.Stop)
|
||||||
|
return
|
||||||
|
} else if v == ki.Stop-1 {
|
||||||
|
i.intervals[k] = NewInterval(ki.Start, ki.Stop-1)
|
||||||
|
return
|
||||||
|
} else if v < ki.Stop-1 {
|
||||||
|
x := NewInterval(ki.Start, v)
|
||||||
|
ki.Start = v + 1
|
||||||
|
// i.intervals.splice(k, 0, x)
|
||||||
|
i.intervals = append(i.intervals[0:k], append([]*Interval{x}, i.intervals[k:]...)...)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *IntervalSet) String() string {
|
||||||
|
return i.StringVerbose(nil, nil, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *IntervalSet) StringVerbose(literalNames []string, symbolicNames []string, elemsAreChar bool) string {
|
||||||
|
|
||||||
|
if i.intervals == nil {
|
||||||
|
return "{}"
|
||||||
|
} else if literalNames != nil || symbolicNames != nil {
|
||||||
|
return i.toTokenString(literalNames, symbolicNames)
|
||||||
|
} else if elemsAreChar {
|
||||||
|
return i.toCharString()
|
||||||
|
}
|
||||||
|
|
||||||
|
return i.toIndexString()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *IntervalSet) GetIntervals() []*Interval {
|
||||||
|
return i.intervals
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *IntervalSet) toCharString() string {
|
||||||
|
names := make([]string, len(i.intervals))
|
||||||
|
|
||||||
|
var sb strings.Builder
|
||||||
|
|
||||||
|
for j := 0; j < len(i.intervals); j++ {
|
||||||
|
v := i.intervals[j]
|
||||||
|
if v.Stop == v.Start+1 {
|
||||||
|
if v.Start == TokenEOF {
|
||||||
|
names = append(names, "<EOF>")
|
||||||
|
} else {
|
||||||
|
sb.WriteByte('\'')
|
||||||
|
sb.WriteRune(rune(v.Start))
|
||||||
|
sb.WriteByte('\'')
|
||||||
|
names = append(names, sb.String())
|
||||||
|
sb.Reset()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sb.WriteByte('\'')
|
||||||
|
sb.WriteRune(rune(v.Start))
|
||||||
|
sb.WriteString("'..'")
|
||||||
|
sb.WriteRune(rune(v.Stop - 1))
|
||||||
|
sb.WriteByte('\'')
|
||||||
|
names = append(names, sb.String())
|
||||||
|
sb.Reset()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(names) > 1 {
|
||||||
|
return "{" + strings.Join(names, ", ") + "}"
|
||||||
|
}
|
||||||
|
|
||||||
|
return names[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *IntervalSet) toIndexString() string {
|
||||||
|
|
||||||
|
names := make([]string, 0)
|
||||||
|
for j := 0; j < len(i.intervals); j++ {
|
||||||
|
v := i.intervals[j]
|
||||||
|
if v.Stop == v.Start+1 {
|
||||||
|
if v.Start == TokenEOF {
|
||||||
|
names = append(names, "<EOF>")
|
||||||
|
} else {
|
||||||
|
names = append(names, strconv.Itoa(v.Start))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
names = append(names, strconv.Itoa(v.Start)+".."+strconv.Itoa(v.Stop-1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(names) > 1 {
|
||||||
|
return "{" + strings.Join(names, ", ") + "}"
|
||||||
|
}
|
||||||
|
|
||||||
|
return names[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *IntervalSet) toTokenString(literalNames []string, symbolicNames []string) string {
|
||||||
|
names := make([]string, 0)
|
||||||
|
for _, v := range i.intervals {
|
||||||
|
for j := v.Start; j < v.Stop; j++ {
|
||||||
|
names = append(names, i.elementName(literalNames, symbolicNames, j))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(names) > 1 {
|
||||||
|
return "{" + strings.Join(names, ", ") + "}"
|
||||||
|
}
|
||||||
|
|
||||||
|
return names[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *IntervalSet) elementName(literalNames []string, symbolicNames []string, a int) string {
|
||||||
|
if a == TokenEOF {
|
||||||
|
return "<EOF>"
|
||||||
|
} else if a == TokenEpsilon {
|
||||||
|
return "<EPSILON>"
|
||||||
|
} else {
|
||||||
|
if a < len(literalNames) && literalNames[a] != "" {
|
||||||
|
return literalNames[a]
|
||||||
|
}
|
||||||
|
|
||||||
|
return symbolicNames[a]
|
||||||
|
}
|
||||||
|
}
|
||||||
198
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/jcollect.go
generated
vendored
Normal file
198
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/jcollect.go
generated
vendored
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
package antlr
|
||||||
|
|
||||||
|
// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
||||||
|
// Use of this file is governed by the BSD 3-clause license that
|
||||||
|
// can be found in the LICENSE.txt file in the project root.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Collectable is an interface that a struct should implement if it is to be
|
||||||
|
// usable as a key in these collections.
|
||||||
|
type Collectable[T any] interface {
|
||||||
|
Hash() int
|
||||||
|
Equals(other Collectable[T]) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type Comparator[T any] interface {
|
||||||
|
Hash1(o T) int
|
||||||
|
Equals2(T, T) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// JStore implements a container that allows the use of a struct to calculate the key
|
||||||
|
// for a collection of values akin to map. This is not meant to be a full-blown HashMap but just
|
||||||
|
// serve the needs of the ANTLR Go runtime.
|
||||||
|
//
|
||||||
|
// For ease of porting the logic of the runtime from the master target (Java), this collection
|
||||||
|
// operates in a similar way to Java, in that it can use any struct that supplies a Hash() and Equals()
|
||||||
|
// function as the key. The values are stored in a standard go map which internally is a form of hashmap
|
||||||
|
// itself, the key for the go map is the hash supplied by the key object. The collection is able to deal with
|
||||||
|
// hash conflicts by using a simple slice of values associated with the hash code indexed bucket. That isn't
|
||||||
|
// particularly efficient, but it is simple, and it works. As this is specifically for the ANTLR runtime, and
|
||||||
|
// we understand the requirements, then this is fine - this is not a general purpose collection.
|
||||||
|
type JStore[T any, C Comparator[T]] struct {
|
||||||
|
store map[int][]T
|
||||||
|
len int
|
||||||
|
comparator Comparator[T]
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewJStore[T any, C Comparator[T]](comparator Comparator[T]) *JStore[T, C] {
|
||||||
|
|
||||||
|
if comparator == nil {
|
||||||
|
panic("comparator cannot be nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
s := &JStore[T, C]{
|
||||||
|
store: make(map[int][]T, 1),
|
||||||
|
comparator: comparator,
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Put will store given value in the collection. Note that the key for storage is generated from
|
||||||
|
// the value itself - this is specifically because that is what ANTLR needs - this would not be useful
|
||||||
|
// as any kind of general collection.
|
||||||
|
//
|
||||||
|
// If the key has a hash conflict, then the value will be added to the slice of values associated with the
|
||||||
|
// hash, unless the value is already in the slice, in which case the existing value is returned. Value equivalence is
|
||||||
|
// tested by calling the equals() method on the key.
|
||||||
|
//
|
||||||
|
// # If the given value is already present in the store, then the existing value is returned as v and exists is set to true
|
||||||
|
//
|
||||||
|
// If the given value is not present in the store, then the value is added to the store and returned as v and exists is set to false.
|
||||||
|
func (s *JStore[T, C]) Put(value T) (v T, exists bool) { //nolint:ireturn
|
||||||
|
|
||||||
|
kh := s.comparator.Hash1(value)
|
||||||
|
|
||||||
|
for _, v1 := range s.store[kh] {
|
||||||
|
if s.comparator.Equals2(value, v1) {
|
||||||
|
return v1, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.store[kh] = append(s.store[kh], value)
|
||||||
|
s.len++
|
||||||
|
return value, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get will return the value associated with the key - the type of the key is the same type as the value
|
||||||
|
// which would not generally be useful, but this is a specific thing for ANTLR where the key is
|
||||||
|
// generated using the object we are going to store.
|
||||||
|
func (s *JStore[T, C]) Get(key T) (T, bool) { //nolint:ireturn
|
||||||
|
|
||||||
|
kh := s.comparator.Hash1(key)
|
||||||
|
|
||||||
|
for _, v := range s.store[kh] {
|
||||||
|
if s.comparator.Equals2(key, v) {
|
||||||
|
return v, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return key, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Contains returns true if the given key is present in the store
|
||||||
|
func (s *JStore[T, C]) Contains(key T) bool { //nolint:ireturn
|
||||||
|
|
||||||
|
_, present := s.Get(key)
|
||||||
|
return present
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *JStore[T, C]) SortedSlice(less func(i, j T) bool) []T {
|
||||||
|
vs := make([]T, 0, len(s.store))
|
||||||
|
for _, v := range s.store {
|
||||||
|
vs = append(vs, v...)
|
||||||
|
}
|
||||||
|
sort.Slice(vs, func(i, j int) bool {
|
||||||
|
return less(vs[i], vs[j])
|
||||||
|
})
|
||||||
|
|
||||||
|
return vs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *JStore[T, C]) Each(f func(T) bool) {
|
||||||
|
for _, e := range s.store {
|
||||||
|
for _, v := range e {
|
||||||
|
f(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *JStore[T, C]) Len() int {
|
||||||
|
return s.len
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *JStore[T, C]) Values() []T {
|
||||||
|
vs := make([]T, 0, len(s.store))
|
||||||
|
for _, e := range s.store {
|
||||||
|
for _, v := range e {
|
||||||
|
vs = append(vs, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return vs
|
||||||
|
}
|
||||||
|
|
||||||
|
type entry[K, V any] struct {
|
||||||
|
key K
|
||||||
|
val V
|
||||||
|
}
|
||||||
|
|
||||||
|
type JMap[K, V any, C Comparator[K]] struct {
|
||||||
|
store map[int][]*entry[K, V]
|
||||||
|
len int
|
||||||
|
comparator Comparator[K]
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewJMap[K, V any, C Comparator[K]](comparator Comparator[K]) *JMap[K, V, C] {
|
||||||
|
return &JMap[K, V, C]{
|
||||||
|
store: make(map[int][]*entry[K, V], 1),
|
||||||
|
comparator: comparator,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *JMap[K, V, C]) Put(key K, val V) {
|
||||||
|
kh := m.comparator.Hash1(key)
|
||||||
|
|
||||||
|
m.store[kh] = append(m.store[kh], &entry[K, V]{key, val})
|
||||||
|
m.len++
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *JMap[K, V, C]) Values() []V {
|
||||||
|
vs := make([]V, 0, len(m.store))
|
||||||
|
for _, e := range m.store {
|
||||||
|
for _, v := range e {
|
||||||
|
vs = append(vs, v.val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return vs
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *JMap[K, V, C]) Get(key K) (V, bool) {
|
||||||
|
|
||||||
|
var none V
|
||||||
|
kh := m.comparator.Hash1(key)
|
||||||
|
for _, e := range m.store[kh] {
|
||||||
|
if m.comparator.Equals2(e.key, key) {
|
||||||
|
return e.val, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return none, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *JMap[K, V, C]) Len() int {
|
||||||
|
return len(m.store)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *JMap[K, V, C]) Delete(key K) {
|
||||||
|
kh := m.comparator.Hash1(key)
|
||||||
|
for i, e := range m.store[kh] {
|
||||||
|
if m.comparator.Equals2(e.key, key) {
|
||||||
|
m.store[kh] = append(m.store[kh][:i], m.store[kh][i+1:]...)
|
||||||
|
m.len--
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *JMap[K, V, C]) Clear() {
|
||||||
|
m.store = make(map[int][]*entry[K, V])
|
||||||
|
}
|
||||||
416
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/lexer.go
generated
vendored
Normal file
416
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/lexer.go
generated
vendored
Normal file
@@ -0,0 +1,416 @@
|
|||||||
|
// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
||||||
|
// Use of this file is governed by the BSD 3-clause license that
|
||||||
|
// can be found in the LICENSE.txt file in the project root.
|
||||||
|
|
||||||
|
package antlr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A lexer is recognizer that draws input symbols from a character stream.
|
||||||
|
// lexer grammars result in a subclass of this object. A Lexer object
|
||||||
|
// uses simplified Match() and error recovery mechanisms in the interest
|
||||||
|
// of speed.
|
||||||
|
///
|
||||||
|
|
||||||
|
type Lexer interface {
|
||||||
|
TokenSource
|
||||||
|
Recognizer
|
||||||
|
|
||||||
|
Emit() Token
|
||||||
|
|
||||||
|
SetChannel(int)
|
||||||
|
PushMode(int)
|
||||||
|
PopMode() int
|
||||||
|
SetType(int)
|
||||||
|
SetMode(int)
|
||||||
|
}
|
||||||
|
|
||||||
|
type BaseLexer struct {
|
||||||
|
*BaseRecognizer
|
||||||
|
|
||||||
|
Interpreter ILexerATNSimulator
|
||||||
|
TokenStartCharIndex int
|
||||||
|
TokenStartLine int
|
||||||
|
TokenStartColumn int
|
||||||
|
ActionType int
|
||||||
|
Virt Lexer // The most derived lexer implementation. Allows virtual method calls.
|
||||||
|
|
||||||
|
input CharStream
|
||||||
|
factory TokenFactory
|
||||||
|
tokenFactorySourcePair *TokenSourceCharStreamPair
|
||||||
|
token Token
|
||||||
|
hitEOF bool
|
||||||
|
channel int
|
||||||
|
thetype int
|
||||||
|
modeStack IntStack
|
||||||
|
mode int
|
||||||
|
text string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBaseLexer(input CharStream) *BaseLexer {
|
||||||
|
|
||||||
|
lexer := new(BaseLexer)
|
||||||
|
|
||||||
|
lexer.BaseRecognizer = NewBaseRecognizer()
|
||||||
|
|
||||||
|
lexer.input = input
|
||||||
|
lexer.factory = CommonTokenFactoryDEFAULT
|
||||||
|
lexer.tokenFactorySourcePair = &TokenSourceCharStreamPair{lexer, input}
|
||||||
|
|
||||||
|
lexer.Virt = lexer
|
||||||
|
|
||||||
|
lexer.Interpreter = nil // child classes must populate it
|
||||||
|
|
||||||
|
// The goal of all lexer rules/methods is to create a token object.
|
||||||
|
// l is an instance variable as multiple rules may collaborate to
|
||||||
|
// create a single token. NextToken will return l object after
|
||||||
|
// Matching lexer rule(s). If you subclass to allow multiple token
|
||||||
|
// emissions, then set l to the last token to be Matched or
|
||||||
|
// something nonnil so that the auto token emit mechanism will not
|
||||||
|
// emit another token.
|
||||||
|
lexer.token = nil
|
||||||
|
|
||||||
|
// What character index in the stream did the current token start at?
|
||||||
|
// Needed, for example, to get the text for current token. Set at
|
||||||
|
// the start of NextToken.
|
||||||
|
lexer.TokenStartCharIndex = -1
|
||||||
|
|
||||||
|
// The line on which the first character of the token resides///
|
||||||
|
lexer.TokenStartLine = -1
|
||||||
|
|
||||||
|
// The character position of first character within the line///
|
||||||
|
lexer.TokenStartColumn = -1
|
||||||
|
|
||||||
|
// Once we see EOF on char stream, next token will be EOF.
|
||||||
|
// If you have DONE : EOF then you see DONE EOF.
|
||||||
|
lexer.hitEOF = false
|
||||||
|
|
||||||
|
// The channel number for the current token///
|
||||||
|
lexer.channel = TokenDefaultChannel
|
||||||
|
|
||||||
|
// The token type for the current token///
|
||||||
|
lexer.thetype = TokenInvalidType
|
||||||
|
|
||||||
|
lexer.modeStack = make([]int, 0)
|
||||||
|
lexer.mode = LexerDefaultMode
|
||||||
|
|
||||||
|
// You can set the text for the current token to override what is in
|
||||||
|
// the input char buffer. Use setText() or can set l instance var.
|
||||||
|
// /
|
||||||
|
lexer.text = ""
|
||||||
|
|
||||||
|
return lexer
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
LexerDefaultMode = 0
|
||||||
|
LexerMore = -2
|
||||||
|
LexerSkip = -3
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
LexerDefaultTokenChannel = TokenDefaultChannel
|
||||||
|
LexerHidden = TokenHiddenChannel
|
||||||
|
LexerMinCharValue = 0x0000
|
||||||
|
LexerMaxCharValue = 0x10FFFF
|
||||||
|
)
|
||||||
|
|
||||||
|
func (b *BaseLexer) reset() {
|
||||||
|
// wack Lexer state variables
|
||||||
|
if b.input != nil {
|
||||||
|
b.input.Seek(0) // rewind the input
|
||||||
|
}
|
||||||
|
b.token = nil
|
||||||
|
b.thetype = TokenInvalidType
|
||||||
|
b.channel = TokenDefaultChannel
|
||||||
|
b.TokenStartCharIndex = -1
|
||||||
|
b.TokenStartColumn = -1
|
||||||
|
b.TokenStartLine = -1
|
||||||
|
b.text = ""
|
||||||
|
|
||||||
|
b.hitEOF = false
|
||||||
|
b.mode = LexerDefaultMode
|
||||||
|
b.modeStack = make([]int, 0)
|
||||||
|
|
||||||
|
b.Interpreter.reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseLexer) GetInterpreter() ILexerATNSimulator {
|
||||||
|
return b.Interpreter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseLexer) GetInputStream() CharStream {
|
||||||
|
return b.input
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseLexer) GetSourceName() string {
|
||||||
|
return b.GrammarFileName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseLexer) SetChannel(v int) {
|
||||||
|
b.channel = v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseLexer) GetTokenFactory() TokenFactory {
|
||||||
|
return b.factory
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseLexer) setTokenFactory(f TokenFactory) {
|
||||||
|
b.factory = f
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseLexer) safeMatch() (ret int) {
|
||||||
|
defer func() {
|
||||||
|
if e := recover(); e != nil {
|
||||||
|
if re, ok := e.(RecognitionException); ok {
|
||||||
|
b.notifyListeners(re) // Report error
|
||||||
|
b.Recover(re)
|
||||||
|
ret = LexerSkip // default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return b.Interpreter.Match(b.input, b.mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return a token from l source i.e., Match a token on the char stream.
|
||||||
|
func (b *BaseLexer) NextToken() Token {
|
||||||
|
if b.input == nil {
|
||||||
|
panic("NextToken requires a non-nil input stream.")
|
||||||
|
}
|
||||||
|
|
||||||
|
tokenStartMarker := b.input.Mark()
|
||||||
|
|
||||||
|
// previously in finally block
|
||||||
|
defer func() {
|
||||||
|
// make sure we release marker after Match or
|
||||||
|
// unbuffered char stream will keep buffering
|
||||||
|
b.input.Release(tokenStartMarker)
|
||||||
|
}()
|
||||||
|
|
||||||
|
for {
|
||||||
|
if b.hitEOF {
|
||||||
|
b.EmitEOF()
|
||||||
|
return b.token
|
||||||
|
}
|
||||||
|
b.token = nil
|
||||||
|
b.channel = TokenDefaultChannel
|
||||||
|
b.TokenStartCharIndex = b.input.Index()
|
||||||
|
b.TokenStartColumn = b.Interpreter.GetCharPositionInLine()
|
||||||
|
b.TokenStartLine = b.Interpreter.GetLine()
|
||||||
|
b.text = ""
|
||||||
|
continueOuter := false
|
||||||
|
for {
|
||||||
|
b.thetype = TokenInvalidType
|
||||||
|
ttype := LexerSkip
|
||||||
|
|
||||||
|
ttype = b.safeMatch()
|
||||||
|
|
||||||
|
if b.input.LA(1) == TokenEOF {
|
||||||
|
b.hitEOF = true
|
||||||
|
}
|
||||||
|
if b.thetype == TokenInvalidType {
|
||||||
|
b.thetype = ttype
|
||||||
|
}
|
||||||
|
if b.thetype == LexerSkip {
|
||||||
|
continueOuter = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if b.thetype != LexerMore {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if continueOuter {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if b.token == nil {
|
||||||
|
b.Virt.Emit()
|
||||||
|
}
|
||||||
|
return b.token
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Instruct the lexer to Skip creating a token for current lexer rule
|
||||||
|
// and look for another token. NextToken() knows to keep looking when
|
||||||
|
// a lexer rule finishes with token set to SKIPTOKEN. Recall that
|
||||||
|
// if token==nil at end of any token rule, it creates one for you
|
||||||
|
// and emits it.
|
||||||
|
// /
|
||||||
|
func (b *BaseLexer) Skip() {
|
||||||
|
b.thetype = LexerSkip
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseLexer) More() {
|
||||||
|
b.thetype = LexerMore
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseLexer) SetMode(m int) {
|
||||||
|
b.mode = m
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseLexer) PushMode(m int) {
|
||||||
|
if LexerATNSimulatorDebug {
|
||||||
|
fmt.Println("pushMode " + strconv.Itoa(m))
|
||||||
|
}
|
||||||
|
b.modeStack.Push(b.mode)
|
||||||
|
b.mode = m
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseLexer) PopMode() int {
|
||||||
|
if len(b.modeStack) == 0 {
|
||||||
|
panic("Empty Stack")
|
||||||
|
}
|
||||||
|
if LexerATNSimulatorDebug {
|
||||||
|
fmt.Println("popMode back to " + fmt.Sprint(b.modeStack[0:len(b.modeStack)-1]))
|
||||||
|
}
|
||||||
|
i, _ := b.modeStack.Pop()
|
||||||
|
b.mode = i
|
||||||
|
return b.mode
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseLexer) inputStream() CharStream {
|
||||||
|
return b.input
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetInputStream resets the lexer input stream and associated lexer state.
|
||||||
|
func (b *BaseLexer) SetInputStream(input CharStream) {
|
||||||
|
b.input = nil
|
||||||
|
b.tokenFactorySourcePair = &TokenSourceCharStreamPair{b, b.input}
|
||||||
|
b.reset()
|
||||||
|
b.input = input
|
||||||
|
b.tokenFactorySourcePair = &TokenSourceCharStreamPair{b, b.input}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseLexer) GetTokenSourceCharStreamPair() *TokenSourceCharStreamPair {
|
||||||
|
return b.tokenFactorySourcePair
|
||||||
|
}
|
||||||
|
|
||||||
|
// By default does not support multiple emits per NextToken invocation
|
||||||
|
// for efficiency reasons. Subclass and override l method, NextToken,
|
||||||
|
// and GetToken (to push tokens into a list and pull from that list
|
||||||
|
// rather than a single variable as l implementation does).
|
||||||
|
// /
|
||||||
|
func (b *BaseLexer) EmitToken(token Token) {
|
||||||
|
b.token = token
|
||||||
|
}
|
||||||
|
|
||||||
|
// The standard method called to automatically emit a token at the
|
||||||
|
// outermost lexical rule. The token object should point into the
|
||||||
|
// char buffer start..stop. If there is a text override in 'text',
|
||||||
|
// use that to set the token's text. Override l method to emit
|
||||||
|
// custom Token objects or provide a Newfactory.
|
||||||
|
// /
|
||||||
|
func (b *BaseLexer) Emit() Token {
|
||||||
|
t := b.factory.Create(b.tokenFactorySourcePair, b.thetype, b.text, b.channel, b.TokenStartCharIndex, b.GetCharIndex()-1, b.TokenStartLine, b.TokenStartColumn)
|
||||||
|
b.EmitToken(t)
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseLexer) EmitEOF() Token {
|
||||||
|
cpos := b.GetCharPositionInLine()
|
||||||
|
lpos := b.GetLine()
|
||||||
|
eof := b.factory.Create(b.tokenFactorySourcePair, TokenEOF, "", TokenDefaultChannel, b.input.Index(), b.input.Index()-1, lpos, cpos)
|
||||||
|
b.EmitToken(eof)
|
||||||
|
return eof
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseLexer) GetCharPositionInLine() int {
|
||||||
|
return b.Interpreter.GetCharPositionInLine()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseLexer) GetLine() int {
|
||||||
|
return b.Interpreter.GetLine()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseLexer) GetType() int {
|
||||||
|
return b.thetype
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseLexer) SetType(t int) {
|
||||||
|
b.thetype = t
|
||||||
|
}
|
||||||
|
|
||||||
|
// What is the index of the current character of lookahead?///
|
||||||
|
func (b *BaseLexer) GetCharIndex() int {
|
||||||
|
return b.input.Index()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the text Matched so far for the current token or any text override.
|
||||||
|
// Set the complete text of l token it wipes any previous changes to the text.
|
||||||
|
func (b *BaseLexer) GetText() string {
|
||||||
|
if b.text != "" {
|
||||||
|
return b.text
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.Interpreter.GetText(b.input)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseLexer) SetText(text string) {
|
||||||
|
b.text = text
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseLexer) GetATN() *ATN {
|
||||||
|
return b.Interpreter.ATN()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return a list of all Token objects in input char stream.
|
||||||
|
// Forces load of all tokens. Does not include EOF token.
|
||||||
|
// /
|
||||||
|
func (b *BaseLexer) GetAllTokens() []Token {
|
||||||
|
vl := b.Virt
|
||||||
|
tokens := make([]Token, 0)
|
||||||
|
t := vl.NextToken()
|
||||||
|
for t.GetTokenType() != TokenEOF {
|
||||||
|
tokens = append(tokens, t)
|
||||||
|
t = vl.NextToken()
|
||||||
|
}
|
||||||
|
return tokens
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseLexer) notifyListeners(e RecognitionException) {
|
||||||
|
start := b.TokenStartCharIndex
|
||||||
|
stop := b.input.Index()
|
||||||
|
text := b.input.GetTextFromInterval(NewInterval(start, stop))
|
||||||
|
msg := "token recognition error at: '" + text + "'"
|
||||||
|
listener := b.GetErrorListenerDispatch()
|
||||||
|
listener.SyntaxError(b, nil, b.TokenStartLine, b.TokenStartColumn, msg, e)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseLexer) getErrorDisplayForChar(c rune) string {
|
||||||
|
if c == TokenEOF {
|
||||||
|
return "<EOF>"
|
||||||
|
} else if c == '\n' {
|
||||||
|
return "\\n"
|
||||||
|
} else if c == '\t' {
|
||||||
|
return "\\t"
|
||||||
|
} else if c == '\r' {
|
||||||
|
return "\\r"
|
||||||
|
} else {
|
||||||
|
return string(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseLexer) getCharErrorDisplay(c rune) string {
|
||||||
|
return "'" + b.getErrorDisplayForChar(c) + "'"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lexers can normally Match any char in it's vocabulary after Matching
|
||||||
|
// a token, so do the easy thing and just kill a character and hope
|
||||||
|
// it all works out. You can instead use the rule invocation stack
|
||||||
|
// to do sophisticated error recovery if you are in a fragment rule.
|
||||||
|
// /
|
||||||
|
func (b *BaseLexer) Recover(re RecognitionException) {
|
||||||
|
if b.input.LA(1) != TokenEOF {
|
||||||
|
if _, ok := re.(*LexerNoViableAltException); ok {
|
||||||
|
// Skip a char and try again
|
||||||
|
b.Interpreter.Consume(b.input)
|
||||||
|
} else {
|
||||||
|
// TODO: Do we lose character or line position information?
|
||||||
|
b.input.Consume()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
432
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/lexer_action.go
generated
vendored
Normal file
432
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/lexer_action.go
generated
vendored
Normal file
@@ -0,0 +1,432 @@
|
|||||||
|
// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
||||||
|
// Use of this file is governed by the BSD 3-clause license that
|
||||||
|
// can be found in the LICENSE.txt file in the project root.
|
||||||
|
|
||||||
|
package antlr
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
const (
|
||||||
|
LexerActionTypeChannel = 0 //The type of a {@link LexerChannelAction} action.
|
||||||
|
LexerActionTypeCustom = 1 //The type of a {@link LexerCustomAction} action.
|
||||||
|
LexerActionTypeMode = 2 //The type of a {@link LexerModeAction} action.
|
||||||
|
LexerActionTypeMore = 3 //The type of a {@link LexerMoreAction} action.
|
||||||
|
LexerActionTypePopMode = 4 //The type of a {@link LexerPopModeAction} action.
|
||||||
|
LexerActionTypePushMode = 5 //The type of a {@link LexerPushModeAction} action.
|
||||||
|
LexerActionTypeSkip = 6 //The type of a {@link LexerSkipAction} action.
|
||||||
|
LexerActionTypeType = 7 //The type of a {@link LexerTypeAction} action.
|
||||||
|
)
|
||||||
|
|
||||||
|
type LexerAction interface {
|
||||||
|
getActionType() int
|
||||||
|
getIsPositionDependent() bool
|
||||||
|
execute(lexer Lexer)
|
||||||
|
Hash() int
|
||||||
|
Equals(other LexerAction) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type BaseLexerAction struct {
|
||||||
|
actionType int
|
||||||
|
isPositionDependent bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBaseLexerAction(action int) *BaseLexerAction {
|
||||||
|
la := new(BaseLexerAction)
|
||||||
|
|
||||||
|
la.actionType = action
|
||||||
|
la.isPositionDependent = false
|
||||||
|
|
||||||
|
return la
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseLexerAction) execute(lexer Lexer) {
|
||||||
|
panic("Not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseLexerAction) getActionType() int {
|
||||||
|
return b.actionType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseLexerAction) getIsPositionDependent() bool {
|
||||||
|
return b.isPositionDependent
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseLexerAction) Hash() int {
|
||||||
|
return b.actionType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseLexerAction) Equals(other LexerAction) bool {
|
||||||
|
return b == other
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implements the {@code Skip} lexer action by calling {@link Lexer//Skip}.
|
||||||
|
//
|
||||||
|
// <p>The {@code Skip} command does not have any parameters, so l action is
|
||||||
|
// implemented as a singleton instance exposed by {@link //INSTANCE}.</p>
|
||||||
|
type LexerSkipAction struct {
|
||||||
|
*BaseLexerAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLexerSkipAction() *LexerSkipAction {
|
||||||
|
la := new(LexerSkipAction)
|
||||||
|
la.BaseLexerAction = NewBaseLexerAction(LexerActionTypeSkip)
|
||||||
|
return la
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provides a singleton instance of l parameterless lexer action.
|
||||||
|
var LexerSkipActionINSTANCE = NewLexerSkipAction()
|
||||||
|
|
||||||
|
func (l *LexerSkipAction) execute(lexer Lexer) {
|
||||||
|
lexer.Skip()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LexerSkipAction) String() string {
|
||||||
|
return "skip"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implements the {@code type} lexer action by calling {@link Lexer//setType}
|
||||||
|
//
|
||||||
|
// with the assigned type.
|
||||||
|
type LexerTypeAction struct {
|
||||||
|
*BaseLexerAction
|
||||||
|
|
||||||
|
thetype int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLexerTypeAction(thetype int) *LexerTypeAction {
|
||||||
|
l := new(LexerTypeAction)
|
||||||
|
l.BaseLexerAction = NewBaseLexerAction(LexerActionTypeType)
|
||||||
|
l.thetype = thetype
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LexerTypeAction) execute(lexer Lexer) {
|
||||||
|
lexer.SetType(l.thetype)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LexerTypeAction) Hash() int {
|
||||||
|
h := murmurInit(0)
|
||||||
|
h = murmurUpdate(h, l.actionType)
|
||||||
|
h = murmurUpdate(h, l.thetype)
|
||||||
|
return murmurFinish(h, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LexerTypeAction) Equals(other LexerAction) bool {
|
||||||
|
if l == other {
|
||||||
|
return true
|
||||||
|
} else if _, ok := other.(*LexerTypeAction); !ok {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
return l.thetype == other.(*LexerTypeAction).thetype
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LexerTypeAction) String() string {
|
||||||
|
return "actionType(" + strconv.Itoa(l.thetype) + ")"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implements the {@code pushMode} lexer action by calling
|
||||||
|
// {@link Lexer//pushMode} with the assigned mode.
|
||||||
|
type LexerPushModeAction struct {
|
||||||
|
*BaseLexerAction
|
||||||
|
|
||||||
|
mode int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLexerPushModeAction(mode int) *LexerPushModeAction {
|
||||||
|
|
||||||
|
l := new(LexerPushModeAction)
|
||||||
|
l.BaseLexerAction = NewBaseLexerAction(LexerActionTypePushMode)
|
||||||
|
|
||||||
|
l.mode = mode
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
// <p>This action is implemented by calling {@link Lexer//pushMode} with the
|
||||||
|
// value provided by {@link //getMode}.</p>
|
||||||
|
func (l *LexerPushModeAction) execute(lexer Lexer) {
|
||||||
|
lexer.PushMode(l.mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LexerPushModeAction) Hash() int {
|
||||||
|
h := murmurInit(0)
|
||||||
|
h = murmurUpdate(h, l.actionType)
|
||||||
|
h = murmurUpdate(h, l.mode)
|
||||||
|
return murmurFinish(h, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LexerPushModeAction) Equals(other LexerAction) bool {
|
||||||
|
if l == other {
|
||||||
|
return true
|
||||||
|
} else if _, ok := other.(*LexerPushModeAction); !ok {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
return l.mode == other.(*LexerPushModeAction).mode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LexerPushModeAction) String() string {
|
||||||
|
return "pushMode(" + strconv.Itoa(l.mode) + ")"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implements the {@code popMode} lexer action by calling {@link Lexer//popMode}.
|
||||||
|
//
|
||||||
|
// <p>The {@code popMode} command does not have any parameters, so l action is
|
||||||
|
// implemented as a singleton instance exposed by {@link //INSTANCE}.</p>
|
||||||
|
type LexerPopModeAction struct {
|
||||||
|
*BaseLexerAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLexerPopModeAction() *LexerPopModeAction {
|
||||||
|
|
||||||
|
l := new(LexerPopModeAction)
|
||||||
|
|
||||||
|
l.BaseLexerAction = NewBaseLexerAction(LexerActionTypePopMode)
|
||||||
|
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
var LexerPopModeActionINSTANCE = NewLexerPopModeAction()
|
||||||
|
|
||||||
|
// <p>This action is implemented by calling {@link Lexer//popMode}.</p>
|
||||||
|
func (l *LexerPopModeAction) execute(lexer Lexer) {
|
||||||
|
lexer.PopMode()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LexerPopModeAction) String() string {
|
||||||
|
return "popMode"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implements the {@code more} lexer action by calling {@link Lexer//more}.
|
||||||
|
//
|
||||||
|
// <p>The {@code more} command does not have any parameters, so l action is
|
||||||
|
// implemented as a singleton instance exposed by {@link //INSTANCE}.</p>
|
||||||
|
|
||||||
|
type LexerMoreAction struct {
|
||||||
|
*BaseLexerAction
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLexerMoreAction() *LexerMoreAction {
|
||||||
|
l := new(LexerMoreAction)
|
||||||
|
l.BaseLexerAction = NewBaseLexerAction(LexerActionTypeMore)
|
||||||
|
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
var LexerMoreActionINSTANCE = NewLexerMoreAction()
|
||||||
|
|
||||||
|
// <p>This action is implemented by calling {@link Lexer//popMode}.</p>
|
||||||
|
func (l *LexerMoreAction) execute(lexer Lexer) {
|
||||||
|
lexer.More()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LexerMoreAction) String() string {
|
||||||
|
return "more"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implements the {@code mode} lexer action by calling {@link Lexer//mode} with
|
||||||
|
// the assigned mode.
|
||||||
|
type LexerModeAction struct {
|
||||||
|
*BaseLexerAction
|
||||||
|
|
||||||
|
mode int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLexerModeAction(mode int) *LexerModeAction {
|
||||||
|
l := new(LexerModeAction)
|
||||||
|
l.BaseLexerAction = NewBaseLexerAction(LexerActionTypeMode)
|
||||||
|
l.mode = mode
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
// <p>This action is implemented by calling {@link Lexer//mode} with the
|
||||||
|
// value provided by {@link //getMode}.</p>
|
||||||
|
func (l *LexerModeAction) execute(lexer Lexer) {
|
||||||
|
lexer.SetMode(l.mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LexerModeAction) Hash() int {
|
||||||
|
h := murmurInit(0)
|
||||||
|
h = murmurUpdate(h, l.actionType)
|
||||||
|
h = murmurUpdate(h, l.mode)
|
||||||
|
return murmurFinish(h, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LexerModeAction) Equals(other LexerAction) bool {
|
||||||
|
if l == other {
|
||||||
|
return true
|
||||||
|
} else if _, ok := other.(*LexerModeAction); !ok {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
return l.mode == other.(*LexerModeAction).mode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LexerModeAction) String() string {
|
||||||
|
return "mode(" + strconv.Itoa(l.mode) + ")"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Executes a custom lexer action by calling {@link Recognizer//action} with the
|
||||||
|
// rule and action indexes assigned to the custom action. The implementation of
|
||||||
|
// a custom action is added to the generated code for the lexer in an override
|
||||||
|
// of {@link Recognizer//action} when the grammar is compiled.
|
||||||
|
//
|
||||||
|
// <p>This class may represent embedded actions created with the <code>{...}</code>
|
||||||
|
// syntax in ANTLR 4, as well as actions created for lexer commands where the
|
||||||
|
// command argument could not be evaluated when the grammar was compiled.</p>
|
||||||
|
|
||||||
|
// Constructs a custom lexer action with the specified rule and action
|
||||||
|
// indexes.
|
||||||
|
//
|
||||||
|
// @param ruleIndex The rule index to use for calls to
|
||||||
|
// {@link Recognizer//action}.
|
||||||
|
// @param actionIndex The action index to use for calls to
|
||||||
|
// {@link Recognizer//action}.
|
||||||
|
|
||||||
|
type LexerCustomAction struct {
|
||||||
|
*BaseLexerAction
|
||||||
|
ruleIndex, actionIndex int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLexerCustomAction(ruleIndex, actionIndex int) *LexerCustomAction {
|
||||||
|
l := new(LexerCustomAction)
|
||||||
|
l.BaseLexerAction = NewBaseLexerAction(LexerActionTypeCustom)
|
||||||
|
l.ruleIndex = ruleIndex
|
||||||
|
l.actionIndex = actionIndex
|
||||||
|
l.isPositionDependent = true
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
// <p>Custom actions are implemented by calling {@link Lexer//action} with the
|
||||||
|
// appropriate rule and action indexes.</p>
|
||||||
|
func (l *LexerCustomAction) execute(lexer Lexer) {
|
||||||
|
lexer.Action(nil, l.ruleIndex, l.actionIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LexerCustomAction) Hash() int {
|
||||||
|
h := murmurInit(0)
|
||||||
|
h = murmurUpdate(h, l.actionType)
|
||||||
|
h = murmurUpdate(h, l.ruleIndex)
|
||||||
|
h = murmurUpdate(h, l.actionIndex)
|
||||||
|
return murmurFinish(h, 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LexerCustomAction) Equals(other LexerAction) bool {
|
||||||
|
if l == other {
|
||||||
|
return true
|
||||||
|
} else if _, ok := other.(*LexerCustomAction); !ok {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
return l.ruleIndex == other.(*LexerCustomAction).ruleIndex &&
|
||||||
|
l.actionIndex == other.(*LexerCustomAction).actionIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implements the {@code channel} lexer action by calling
|
||||||
|
// {@link Lexer//setChannel} with the assigned channel.
|
||||||
|
// Constructs a New{@code channel} action with the specified channel value.
|
||||||
|
// @param channel The channel value to pass to {@link Lexer//setChannel}.
|
||||||
|
type LexerChannelAction struct {
|
||||||
|
*BaseLexerAction
|
||||||
|
|
||||||
|
channel int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLexerChannelAction(channel int) *LexerChannelAction {
|
||||||
|
l := new(LexerChannelAction)
|
||||||
|
l.BaseLexerAction = NewBaseLexerAction(LexerActionTypeChannel)
|
||||||
|
l.channel = channel
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
// <p>This action is implemented by calling {@link Lexer//setChannel} with the
|
||||||
|
// value provided by {@link //getChannel}.</p>
|
||||||
|
func (l *LexerChannelAction) execute(lexer Lexer) {
|
||||||
|
lexer.SetChannel(l.channel)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LexerChannelAction) Hash() int {
|
||||||
|
h := murmurInit(0)
|
||||||
|
h = murmurUpdate(h, l.actionType)
|
||||||
|
h = murmurUpdate(h, l.channel)
|
||||||
|
return murmurFinish(h, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LexerChannelAction) Equals(other LexerAction) bool {
|
||||||
|
if l == other {
|
||||||
|
return true
|
||||||
|
} else if _, ok := other.(*LexerChannelAction); !ok {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
return l.channel == other.(*LexerChannelAction).channel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LexerChannelAction) String() string {
|
||||||
|
return "channel(" + strconv.Itoa(l.channel) + ")"
|
||||||
|
}
|
||||||
|
|
||||||
|
// This implementation of {@link LexerAction} is used for tracking input offsets
|
||||||
|
// for position-dependent actions within a {@link LexerActionExecutor}.
|
||||||
|
//
|
||||||
|
// <p>This action is not serialized as part of the ATN, and is only required for
|
||||||
|
// position-dependent lexer actions which appear at a location other than the
|
||||||
|
// end of a rule. For more information about DFA optimizations employed for
|
||||||
|
// lexer actions, see {@link LexerActionExecutor//append} and
|
||||||
|
// {@link LexerActionExecutor//fixOffsetBeforeMatch}.</p>
|
||||||
|
|
||||||
|
// Constructs a Newindexed custom action by associating a character offset
|
||||||
|
// with a {@link LexerAction}.
|
||||||
|
//
|
||||||
|
// <p>Note: This class is only required for lexer actions for which
|
||||||
|
// {@link LexerAction//isPositionDependent} returns {@code true}.</p>
|
||||||
|
//
|
||||||
|
// @param offset The offset into the input {@link CharStream}, relative to
|
||||||
|
// the token start index, at which the specified lexer action should be
|
||||||
|
// executed.
|
||||||
|
// @param action The lexer action to execute at a particular offset in the
|
||||||
|
// input {@link CharStream}.
|
||||||
|
type LexerIndexedCustomAction struct {
|
||||||
|
*BaseLexerAction
|
||||||
|
|
||||||
|
offset int
|
||||||
|
lexerAction LexerAction
|
||||||
|
isPositionDependent bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLexerIndexedCustomAction(offset int, lexerAction LexerAction) *LexerIndexedCustomAction {
|
||||||
|
|
||||||
|
l := new(LexerIndexedCustomAction)
|
||||||
|
l.BaseLexerAction = NewBaseLexerAction(lexerAction.getActionType())
|
||||||
|
|
||||||
|
l.offset = offset
|
||||||
|
l.lexerAction = lexerAction
|
||||||
|
l.isPositionDependent = true
|
||||||
|
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
// <p>This method calls {@link //execute} on the result of {@link //getAction}
|
||||||
|
// using the provided {@code lexer}.</p>
|
||||||
|
func (l *LexerIndexedCustomAction) execute(lexer Lexer) {
|
||||||
|
// assume the input stream position was properly set by the calling code
|
||||||
|
l.lexerAction.execute(lexer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LexerIndexedCustomAction) Hash() int {
|
||||||
|
h := murmurInit(0)
|
||||||
|
h = murmurUpdate(h, l.offset)
|
||||||
|
h = murmurUpdate(h, l.lexerAction.Hash())
|
||||||
|
return murmurFinish(h, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LexerIndexedCustomAction) equals(other LexerAction) bool {
|
||||||
|
if l == other {
|
||||||
|
return true
|
||||||
|
} else if _, ok := other.(*LexerIndexedCustomAction); !ok {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
return l.offset == other.(*LexerIndexedCustomAction).offset &&
|
||||||
|
l.lexerAction.Equals(other.(*LexerIndexedCustomAction).lexerAction)
|
||||||
|
}
|
||||||
|
}
|
||||||
186
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/lexer_action_executor.go
generated
vendored
Normal file
186
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/lexer_action_executor.go
generated
vendored
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
||||||
|
// Use of this file is governed by the BSD 3-clause license that
|
||||||
|
// can be found in the LICENSE.txt file in the project root.
|
||||||
|
|
||||||
|
package antlr
|
||||||
|
|
||||||
|
import "golang.org/x/exp/slices"
|
||||||
|
|
||||||
|
// Represents an executor for a sequence of lexer actions which traversed during
|
||||||
|
// the Matching operation of a lexer rule (token).
|
||||||
|
//
|
||||||
|
// <p>The executor tracks position information for position-dependent lexer actions
|
||||||
|
// efficiently, ensuring that actions appearing only at the end of the rule do
|
||||||
|
// not cause bloating of the {@link DFA} created for the lexer.</p>
|
||||||
|
|
||||||
|
type LexerActionExecutor struct {
|
||||||
|
lexerActions []LexerAction
|
||||||
|
cachedHash int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLexerActionExecutor(lexerActions []LexerAction) *LexerActionExecutor {
|
||||||
|
|
||||||
|
if lexerActions == nil {
|
||||||
|
lexerActions = make([]LexerAction, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
l := new(LexerActionExecutor)
|
||||||
|
|
||||||
|
l.lexerActions = lexerActions
|
||||||
|
|
||||||
|
// Caches the result of {@link //hashCode} since the hash code is an element
|
||||||
|
// of the performance-critical {@link LexerATNConfig//hashCode} operation.
|
||||||
|
l.cachedHash = murmurInit(57)
|
||||||
|
for _, a := range lexerActions {
|
||||||
|
l.cachedHash = murmurUpdate(l.cachedHash, a.Hash())
|
||||||
|
}
|
||||||
|
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a {@link LexerActionExecutor} which executes the actions for
|
||||||
|
// the input {@code lexerActionExecutor} followed by a specified
|
||||||
|
// {@code lexerAction}.
|
||||||
|
//
|
||||||
|
// @param lexerActionExecutor The executor for actions already traversed by
|
||||||
|
// the lexer while Matching a token within a particular
|
||||||
|
// {@link LexerATNConfig}. If this is {@code nil}, the method behaves as
|
||||||
|
// though it were an empty executor.
|
||||||
|
// @param lexerAction The lexer action to execute after the actions
|
||||||
|
// specified in {@code lexerActionExecutor}.
|
||||||
|
//
|
||||||
|
// @return A {@link LexerActionExecutor} for executing the combine actions
|
||||||
|
// of {@code lexerActionExecutor} and {@code lexerAction}.
|
||||||
|
func LexerActionExecutorappend(lexerActionExecutor *LexerActionExecutor, lexerAction LexerAction) *LexerActionExecutor {
|
||||||
|
if lexerActionExecutor == nil {
|
||||||
|
return NewLexerActionExecutor([]LexerAction{lexerAction})
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewLexerActionExecutor(append(lexerActionExecutor.lexerActions, lexerAction))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a {@link LexerActionExecutor} which encodes the current offset
|
||||||
|
// for position-dependent lexer actions.
|
||||||
|
//
|
||||||
|
// <p>Normally, when the executor encounters lexer actions where
|
||||||
|
// {@link LexerAction//isPositionDependent} returns {@code true}, it calls
|
||||||
|
// {@link IntStream//seek} on the input {@link CharStream} to set the input
|
||||||
|
// position to the <em>end</em> of the current token. This behavior provides
|
||||||
|
// for efficient DFA representation of lexer actions which appear at the end
|
||||||
|
// of a lexer rule, even when the lexer rule Matches a variable number of
|
||||||
|
// characters.</p>
|
||||||
|
//
|
||||||
|
// <p>Prior to traversing a Match transition in the ATN, the current offset
|
||||||
|
// from the token start index is assigned to all position-dependent lexer
|
||||||
|
// actions which have not already been assigned a fixed offset. By storing
|
||||||
|
// the offsets relative to the token start index, the DFA representation of
|
||||||
|
// lexer actions which appear in the middle of tokens remains efficient due
|
||||||
|
// to sharing among tokens of the same length, regardless of their absolute
|
||||||
|
// position in the input stream.</p>
|
||||||
|
//
|
||||||
|
// <p>If the current executor already has offsets assigned to all
|
||||||
|
// position-dependent lexer actions, the method returns {@code this}.</p>
|
||||||
|
//
|
||||||
|
// @param offset The current offset to assign to all position-dependent
|
||||||
|
// lexer actions which do not already have offsets assigned.
|
||||||
|
//
|
||||||
|
// @return A {@link LexerActionExecutor} which stores input stream offsets
|
||||||
|
// for all position-dependent lexer actions.
|
||||||
|
// /
|
||||||
|
func (l *LexerActionExecutor) fixOffsetBeforeMatch(offset int) *LexerActionExecutor {
|
||||||
|
var updatedLexerActions []LexerAction
|
||||||
|
for i := 0; i < len(l.lexerActions); i++ {
|
||||||
|
_, ok := l.lexerActions[i].(*LexerIndexedCustomAction)
|
||||||
|
if l.lexerActions[i].getIsPositionDependent() && !ok {
|
||||||
|
if updatedLexerActions == nil {
|
||||||
|
updatedLexerActions = make([]LexerAction, 0)
|
||||||
|
|
||||||
|
for _, a := range l.lexerActions {
|
||||||
|
updatedLexerActions = append(updatedLexerActions, a)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updatedLexerActions[i] = NewLexerIndexedCustomAction(offset, l.lexerActions[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if updatedLexerActions == nil {
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewLexerActionExecutor(updatedLexerActions)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute the actions encapsulated by l executor within the context of a
|
||||||
|
// particular {@link Lexer}.
|
||||||
|
//
|
||||||
|
// <p>This method calls {@link IntStream//seek} to set the position of the
|
||||||
|
// {@code input} {@link CharStream} prior to calling
|
||||||
|
// {@link LexerAction//execute} on a position-dependent action. Before the
|
||||||
|
// method returns, the input position will be restored to the same position
|
||||||
|
// it was in when the method was invoked.</p>
|
||||||
|
//
|
||||||
|
// @param lexer The lexer instance.
|
||||||
|
// @param input The input stream which is the source for the current token.
|
||||||
|
// When l method is called, the current {@link IntStream//index} for
|
||||||
|
// {@code input} should be the start of the following token, i.e. 1
|
||||||
|
// character past the end of the current token.
|
||||||
|
// @param startIndex The token start index. This value may be passed to
|
||||||
|
// {@link IntStream//seek} to set the {@code input} position to the beginning
|
||||||
|
// of the token.
|
||||||
|
// /
|
||||||
|
func (l *LexerActionExecutor) execute(lexer Lexer, input CharStream, startIndex int) {
|
||||||
|
requiresSeek := false
|
||||||
|
stopIndex := input.Index()
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if requiresSeek {
|
||||||
|
input.Seek(stopIndex)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
for i := 0; i < len(l.lexerActions); i++ {
|
||||||
|
lexerAction := l.lexerActions[i]
|
||||||
|
if la, ok := lexerAction.(*LexerIndexedCustomAction); ok {
|
||||||
|
offset := la.offset
|
||||||
|
input.Seek(startIndex + offset)
|
||||||
|
lexerAction = la.lexerAction
|
||||||
|
requiresSeek = (startIndex + offset) != stopIndex
|
||||||
|
} else if lexerAction.getIsPositionDependent() {
|
||||||
|
input.Seek(stopIndex)
|
||||||
|
requiresSeek = false
|
||||||
|
}
|
||||||
|
lexerAction.execute(lexer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LexerActionExecutor) Hash() int {
|
||||||
|
if l == nil {
|
||||||
|
// TODO: Why is this here? l should not be nil
|
||||||
|
return 61
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: This is created from the action itself when the struct is created - will this be an issue at some point? Java uses the runtime assign hashcode
|
||||||
|
return l.cachedHash
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LexerActionExecutor) Equals(other interface{}) bool {
|
||||||
|
if l == other {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
othert, ok := other.(*LexerActionExecutor)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if othert == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if l.cachedHash != othert.cachedHash {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(l.lexerActions) != len(othert.lexerActions) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return slices.EqualFunc(l.lexerActions, othert.lexerActions, func(i, j LexerAction) bool {
|
||||||
|
return i.Equals(j)
|
||||||
|
})
|
||||||
|
}
|
||||||
684
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/lexer_atn_simulator.go
generated
vendored
Normal file
684
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/lexer_atn_simulator.go
generated
vendored
Normal file
@@ -0,0 +1,684 @@
|
|||||||
|
// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
||||||
|
// Use of this file is governed by the BSD 3-clause license that
|
||||||
|
// can be found in the LICENSE.txt file in the project root.
|
||||||
|
|
||||||
|
package antlr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
LexerATNSimulatorDebug = false
|
||||||
|
LexerATNSimulatorDFADebug = false
|
||||||
|
|
||||||
|
LexerATNSimulatorMinDFAEdge = 0
|
||||||
|
LexerATNSimulatorMaxDFAEdge = 127 // forces unicode to stay in ATN
|
||||||
|
|
||||||
|
LexerATNSimulatorMatchCalls = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
type ILexerATNSimulator interface {
|
||||||
|
IATNSimulator
|
||||||
|
|
||||||
|
reset()
|
||||||
|
Match(input CharStream, mode int) int
|
||||||
|
GetCharPositionInLine() int
|
||||||
|
GetLine() int
|
||||||
|
GetText(input CharStream) string
|
||||||
|
Consume(input CharStream)
|
||||||
|
}
|
||||||
|
|
||||||
|
type LexerATNSimulator struct {
|
||||||
|
*BaseATNSimulator
|
||||||
|
|
||||||
|
recog Lexer
|
||||||
|
predictionMode int
|
||||||
|
mergeCache DoubleDict
|
||||||
|
startIndex int
|
||||||
|
Line int
|
||||||
|
CharPositionInLine int
|
||||||
|
mode int
|
||||||
|
prevAccept *SimState
|
||||||
|
MatchCalls int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLexerATNSimulator(recog Lexer, atn *ATN, decisionToDFA []*DFA, sharedContextCache *PredictionContextCache) *LexerATNSimulator {
|
||||||
|
l := new(LexerATNSimulator)
|
||||||
|
|
||||||
|
l.BaseATNSimulator = NewBaseATNSimulator(atn, sharedContextCache)
|
||||||
|
|
||||||
|
l.decisionToDFA = decisionToDFA
|
||||||
|
l.recog = recog
|
||||||
|
// The current token's starting index into the character stream.
|
||||||
|
// Shared across DFA to ATN simulation in case the ATN fails and the
|
||||||
|
// DFA did not have a previous accept state. In l case, we use the
|
||||||
|
// ATN-generated exception object.
|
||||||
|
l.startIndex = -1
|
||||||
|
// line number 1..n within the input///
|
||||||
|
l.Line = 1
|
||||||
|
// The index of the character relative to the beginning of the line
|
||||||
|
// 0..n-1///
|
||||||
|
l.CharPositionInLine = 0
|
||||||
|
l.mode = LexerDefaultMode
|
||||||
|
// Used during DFA/ATN exec to record the most recent accept configuration
|
||||||
|
// info
|
||||||
|
l.prevAccept = NewSimState()
|
||||||
|
// done
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LexerATNSimulator) copyState(simulator *LexerATNSimulator) {
|
||||||
|
l.CharPositionInLine = simulator.CharPositionInLine
|
||||||
|
l.Line = simulator.Line
|
||||||
|
l.mode = simulator.mode
|
||||||
|
l.startIndex = simulator.startIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LexerATNSimulator) Match(input CharStream, mode int) int {
|
||||||
|
l.MatchCalls++
|
||||||
|
l.mode = mode
|
||||||
|
mark := input.Mark()
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
input.Release(mark)
|
||||||
|
}()
|
||||||
|
|
||||||
|
l.startIndex = input.Index()
|
||||||
|
l.prevAccept.reset()
|
||||||
|
|
||||||
|
dfa := l.decisionToDFA[mode]
|
||||||
|
|
||||||
|
var s0 *DFAState
|
||||||
|
l.atn.stateMu.RLock()
|
||||||
|
s0 = dfa.getS0()
|
||||||
|
l.atn.stateMu.RUnlock()
|
||||||
|
|
||||||
|
if s0 == nil {
|
||||||
|
return l.MatchATN(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
return l.execATN(input, s0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LexerATNSimulator) reset() {
|
||||||
|
l.prevAccept.reset()
|
||||||
|
l.startIndex = -1
|
||||||
|
l.Line = 1
|
||||||
|
l.CharPositionInLine = 0
|
||||||
|
l.mode = LexerDefaultMode
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LexerATNSimulator) MatchATN(input CharStream) int {
|
||||||
|
startState := l.atn.modeToStartState[l.mode]
|
||||||
|
|
||||||
|
if LexerATNSimulatorDebug {
|
||||||
|
fmt.Println("MatchATN mode " + strconv.Itoa(l.mode) + " start: " + startState.String())
|
||||||
|
}
|
||||||
|
oldMode := l.mode
|
||||||
|
s0Closure := l.computeStartState(input, startState)
|
||||||
|
suppressEdge := s0Closure.hasSemanticContext
|
||||||
|
s0Closure.hasSemanticContext = false
|
||||||
|
|
||||||
|
next := l.addDFAState(s0Closure, suppressEdge)
|
||||||
|
|
||||||
|
predict := l.execATN(input, next)
|
||||||
|
|
||||||
|
if LexerATNSimulatorDebug {
|
||||||
|
fmt.Println("DFA after MatchATN: " + l.decisionToDFA[oldMode].ToLexerString())
|
||||||
|
}
|
||||||
|
return predict
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LexerATNSimulator) execATN(input CharStream, ds0 *DFAState) int {
|
||||||
|
|
||||||
|
if LexerATNSimulatorDebug {
|
||||||
|
fmt.Println("start state closure=" + ds0.configs.String())
|
||||||
|
}
|
||||||
|
if ds0.isAcceptState {
|
||||||
|
// allow zero-length tokens
|
||||||
|
l.captureSimState(l.prevAccept, input, ds0)
|
||||||
|
}
|
||||||
|
t := input.LA(1)
|
||||||
|
s := ds0 // s is current/from DFA state
|
||||||
|
|
||||||
|
for { // while more work
|
||||||
|
if LexerATNSimulatorDebug {
|
||||||
|
fmt.Println("execATN loop starting closure: " + s.configs.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// As we move src->trg, src->trg, we keep track of the previous trg to
|
||||||
|
// avoid looking up the DFA state again, which is expensive.
|
||||||
|
// If the previous target was already part of the DFA, we might
|
||||||
|
// be able to avoid doing a reach operation upon t. If s!=nil,
|
||||||
|
// it means that semantic predicates didn't prevent us from
|
||||||
|
// creating a DFA state. Once we know s!=nil, we check to see if
|
||||||
|
// the DFA state has an edge already for t. If so, we can just reuse
|
||||||
|
// it's configuration set there's no point in re-computing it.
|
||||||
|
// This is kind of like doing DFA simulation within the ATN
|
||||||
|
// simulation because DFA simulation is really just a way to avoid
|
||||||
|
// computing reach/closure sets. Technically, once we know that
|
||||||
|
// we have a previously added DFA state, we could jump over to
|
||||||
|
// the DFA simulator. But, that would mean popping back and forth
|
||||||
|
// a lot and making things more complicated algorithmically.
|
||||||
|
// This optimization makes a lot of sense for loops within DFA.
|
||||||
|
// A character will take us back to an existing DFA state
|
||||||
|
// that already has lots of edges out of it. e.g., .* in comments.
|
||||||
|
target := l.getExistingTargetState(s, t)
|
||||||
|
if target == nil {
|
||||||
|
target = l.computeTargetState(input, s, t)
|
||||||
|
// print("Computed:" + str(target))
|
||||||
|
}
|
||||||
|
if target == ATNSimulatorError {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// If l is a consumable input element, make sure to consume before
|
||||||
|
// capturing the accept state so the input index, line, and char
|
||||||
|
// position accurately reflect the state of the interpreter at the
|
||||||
|
// end of the token.
|
||||||
|
if t != TokenEOF {
|
||||||
|
l.Consume(input)
|
||||||
|
}
|
||||||
|
if target.isAcceptState {
|
||||||
|
l.captureSimState(l.prevAccept, input, target)
|
||||||
|
if t == TokenEOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t = input.LA(1)
|
||||||
|
s = target // flip current DFA target becomes Newsrc/from state
|
||||||
|
}
|
||||||
|
|
||||||
|
return l.failOrAccept(l.prevAccept, input, s.configs, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get an existing target state for an edge in the DFA. If the target state
|
||||||
|
// for the edge has not yet been computed or is otherwise not available,
|
||||||
|
// l method returns {@code nil}.
|
||||||
|
//
|
||||||
|
// @param s The current DFA state
|
||||||
|
// @param t The next input symbol
|
||||||
|
// @return The existing target DFA state for the given input symbol
|
||||||
|
// {@code t}, or {@code nil} if the target state for l edge is not
|
||||||
|
// already cached
|
||||||
|
func (l *LexerATNSimulator) getExistingTargetState(s *DFAState, t int) *DFAState {
|
||||||
|
if t < LexerATNSimulatorMinDFAEdge || t > LexerATNSimulatorMaxDFAEdge {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
l.atn.edgeMu.RLock()
|
||||||
|
defer l.atn.edgeMu.RUnlock()
|
||||||
|
if s.getEdges() == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
target := s.getIthEdge(t - LexerATNSimulatorMinDFAEdge)
|
||||||
|
if LexerATNSimulatorDebug && target != nil {
|
||||||
|
fmt.Println("reuse state " + strconv.Itoa(s.stateNumber) + " edge to " + strconv.Itoa(target.stateNumber))
|
||||||
|
}
|
||||||
|
return target
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute a target state for an edge in the DFA, and attempt to add the
|
||||||
|
// computed state and corresponding edge to the DFA.
|
||||||
|
//
|
||||||
|
// @param input The input stream
|
||||||
|
// @param s The current DFA state
|
||||||
|
// @param t The next input symbol
|
||||||
|
//
|
||||||
|
// @return The computed target DFA state for the given input symbol
|
||||||
|
// {@code t}. If {@code t} does not lead to a valid DFA state, l method
|
||||||
|
// returns {@link //ERROR}.
|
||||||
|
func (l *LexerATNSimulator) computeTargetState(input CharStream, s *DFAState, t int) *DFAState {
|
||||||
|
reach := NewOrderedATNConfigSet()
|
||||||
|
|
||||||
|
// if we don't find an existing DFA state
|
||||||
|
// Fill reach starting from closure, following t transitions
|
||||||
|
l.getReachableConfigSet(input, s.configs, reach.BaseATNConfigSet, t)
|
||||||
|
|
||||||
|
if len(reach.configs) == 0 { // we got nowhere on t from s
|
||||||
|
if !reach.hasSemanticContext {
|
||||||
|
// we got nowhere on t, don't panic out l knowledge it'd
|
||||||
|
// cause a failover from DFA later.
|
||||||
|
l.addDFAEdge(s, t, ATNSimulatorError, nil)
|
||||||
|
}
|
||||||
|
// stop when we can't Match any more char
|
||||||
|
return ATNSimulatorError
|
||||||
|
}
|
||||||
|
// Add an edge from s to target DFA found/created for reach
|
||||||
|
return l.addDFAEdge(s, t, nil, reach.BaseATNConfigSet)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LexerATNSimulator) failOrAccept(prevAccept *SimState, input CharStream, reach ATNConfigSet, t int) int {
|
||||||
|
if l.prevAccept.dfaState != nil {
|
||||||
|
lexerActionExecutor := prevAccept.dfaState.lexerActionExecutor
|
||||||
|
l.accept(input, lexerActionExecutor, l.startIndex, prevAccept.index, prevAccept.line, prevAccept.column)
|
||||||
|
return prevAccept.dfaState.prediction
|
||||||
|
}
|
||||||
|
|
||||||
|
// if no accept and EOF is first char, return EOF
|
||||||
|
if t == TokenEOF && input.Index() == l.startIndex {
|
||||||
|
return TokenEOF
|
||||||
|
}
|
||||||
|
|
||||||
|
panic(NewLexerNoViableAltException(l.recog, input, l.startIndex, reach))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given a starting configuration set, figure out all ATN configurations
|
||||||
|
// we can reach upon input {@code t}. Parameter {@code reach} is a return
|
||||||
|
// parameter.
|
||||||
|
func (l *LexerATNSimulator) getReachableConfigSet(input CharStream, closure ATNConfigSet, reach ATNConfigSet, t int) {
|
||||||
|
// l is used to Skip processing for configs which have a lower priority
|
||||||
|
// than a config that already reached an accept state for the same rule
|
||||||
|
SkipAlt := ATNInvalidAltNumber
|
||||||
|
|
||||||
|
for _, cfg := range closure.GetItems() {
|
||||||
|
currentAltReachedAcceptState := (cfg.GetAlt() == SkipAlt)
|
||||||
|
if currentAltReachedAcceptState && cfg.(*LexerATNConfig).passedThroughNonGreedyDecision {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if LexerATNSimulatorDebug {
|
||||||
|
|
||||||
|
fmt.Printf("testing %s at %s\n", l.GetTokenName(t), cfg.String()) // l.recog, true))
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, trans := range cfg.GetState().GetTransitions() {
|
||||||
|
target := l.getReachableTarget(trans, t)
|
||||||
|
if target != nil {
|
||||||
|
lexerActionExecutor := cfg.(*LexerATNConfig).lexerActionExecutor
|
||||||
|
if lexerActionExecutor != nil {
|
||||||
|
lexerActionExecutor = lexerActionExecutor.fixOffsetBeforeMatch(input.Index() - l.startIndex)
|
||||||
|
}
|
||||||
|
treatEOFAsEpsilon := (t == TokenEOF)
|
||||||
|
config := NewLexerATNConfig3(cfg.(*LexerATNConfig), target, lexerActionExecutor)
|
||||||
|
if l.closure(input, config, reach,
|
||||||
|
currentAltReachedAcceptState, true, treatEOFAsEpsilon) {
|
||||||
|
// any remaining configs for l alt have a lower priority
|
||||||
|
// than the one that just reached an accept state.
|
||||||
|
SkipAlt = cfg.GetAlt()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LexerATNSimulator) accept(input CharStream, lexerActionExecutor *LexerActionExecutor, startIndex, index, line, charPos int) {
|
||||||
|
if LexerATNSimulatorDebug {
|
||||||
|
fmt.Printf("ACTION %v\n", lexerActionExecutor)
|
||||||
|
}
|
||||||
|
// seek to after last char in token
|
||||||
|
input.Seek(index)
|
||||||
|
l.Line = line
|
||||||
|
l.CharPositionInLine = charPos
|
||||||
|
if lexerActionExecutor != nil && l.recog != nil {
|
||||||
|
lexerActionExecutor.execute(l.recog, input, startIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LexerATNSimulator) getReachableTarget(trans Transition, t int) ATNState {
|
||||||
|
if trans.Matches(t, 0, LexerMaxCharValue) {
|
||||||
|
return trans.getTarget()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LexerATNSimulator) computeStartState(input CharStream, p ATNState) *OrderedATNConfigSet {
|
||||||
|
configs := NewOrderedATNConfigSet()
|
||||||
|
for i := 0; i < len(p.GetTransitions()); i++ {
|
||||||
|
target := p.GetTransitions()[i].getTarget()
|
||||||
|
cfg := NewLexerATNConfig6(target, i+1, BasePredictionContextEMPTY)
|
||||||
|
l.closure(input, cfg, configs, false, false, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return configs
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since the alternatives within any lexer decision are ordered by
|
||||||
|
// preference, l method stops pursuing the closure as soon as an accept
|
||||||
|
// state is reached. After the first accept state is reached by depth-first
|
||||||
|
// search from {@code config}, all other (potentially reachable) states for
|
||||||
|
// l rule would have a lower priority.
|
||||||
|
//
|
||||||
|
// @return {@code true} if an accept state is reached, otherwise
|
||||||
|
// {@code false}.
|
||||||
|
func (l *LexerATNSimulator) closure(input CharStream, config *LexerATNConfig, configs ATNConfigSet,
|
||||||
|
currentAltReachedAcceptState, speculative, treatEOFAsEpsilon bool) bool {
|
||||||
|
|
||||||
|
if LexerATNSimulatorDebug {
|
||||||
|
fmt.Println("closure(" + config.String() + ")") // config.String(l.recog, true) + ")")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, ok := config.state.(*RuleStopState)
|
||||||
|
if ok {
|
||||||
|
|
||||||
|
if LexerATNSimulatorDebug {
|
||||||
|
if l.recog != nil {
|
||||||
|
fmt.Printf("closure at %s rule stop %s\n", l.recog.GetRuleNames()[config.state.GetRuleIndex()], config)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("closure at rule stop %s\n", config)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.context == nil || config.context.hasEmptyPath() {
|
||||||
|
if config.context == nil || config.context.isEmpty() {
|
||||||
|
configs.Add(config, nil)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
configs.Add(NewLexerATNConfig2(config, config.state, BasePredictionContextEMPTY), nil)
|
||||||
|
currentAltReachedAcceptState = true
|
||||||
|
}
|
||||||
|
if config.context != nil && !config.context.isEmpty() {
|
||||||
|
for i := 0; i < config.context.length(); i++ {
|
||||||
|
if config.context.getReturnState(i) != BasePredictionContextEmptyReturnState {
|
||||||
|
newContext := config.context.GetParent(i) // "pop" return state
|
||||||
|
returnState := l.atn.states[config.context.getReturnState(i)]
|
||||||
|
cfg := NewLexerATNConfig2(config, returnState, newContext)
|
||||||
|
currentAltReachedAcceptState = l.closure(input, cfg, configs, currentAltReachedAcceptState, speculative, treatEOFAsEpsilon)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return currentAltReachedAcceptState
|
||||||
|
}
|
||||||
|
// optimization
|
||||||
|
if !config.state.GetEpsilonOnlyTransitions() {
|
||||||
|
if !currentAltReachedAcceptState || !config.passedThroughNonGreedyDecision {
|
||||||
|
configs.Add(config, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for j := 0; j < len(config.state.GetTransitions()); j++ {
|
||||||
|
trans := config.state.GetTransitions()[j]
|
||||||
|
cfg := l.getEpsilonTarget(input, config, trans, configs, speculative, treatEOFAsEpsilon)
|
||||||
|
if cfg != nil {
|
||||||
|
currentAltReachedAcceptState = l.closure(input, cfg, configs,
|
||||||
|
currentAltReachedAcceptState, speculative, treatEOFAsEpsilon)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return currentAltReachedAcceptState
|
||||||
|
}
|
||||||
|
|
||||||
|
// side-effect: can alter configs.hasSemanticContext
|
||||||
|
func (l *LexerATNSimulator) getEpsilonTarget(input CharStream, config *LexerATNConfig, trans Transition,
|
||||||
|
configs ATNConfigSet, speculative, treatEOFAsEpsilon bool) *LexerATNConfig {
|
||||||
|
|
||||||
|
var cfg *LexerATNConfig
|
||||||
|
|
||||||
|
if trans.getSerializationType() == TransitionRULE {
|
||||||
|
|
||||||
|
rt := trans.(*RuleTransition)
|
||||||
|
newContext := SingletonBasePredictionContextCreate(config.context, rt.followState.GetStateNumber())
|
||||||
|
cfg = NewLexerATNConfig2(config, trans.getTarget(), newContext)
|
||||||
|
|
||||||
|
} else if trans.getSerializationType() == TransitionPRECEDENCE {
|
||||||
|
panic("Precedence predicates are not supported in lexers.")
|
||||||
|
} else if trans.getSerializationType() == TransitionPREDICATE {
|
||||||
|
// Track traversing semantic predicates. If we traverse,
|
||||||
|
// we cannot add a DFA state for l "reach" computation
|
||||||
|
// because the DFA would not test the predicate again in the
|
||||||
|
// future. Rather than creating collections of semantic predicates
|
||||||
|
// like v3 and testing them on prediction, v4 will test them on the
|
||||||
|
// fly all the time using the ATN not the DFA. This is slower but
|
||||||
|
// semantically it's not used that often. One of the key elements to
|
||||||
|
// l predicate mechanism is not adding DFA states that see
|
||||||
|
// predicates immediately afterwards in the ATN. For example,
|
||||||
|
|
||||||
|
// a : ID {p1}? | ID {p2}?
|
||||||
|
|
||||||
|
// should create the start state for rule 'a' (to save start state
|
||||||
|
// competition), but should not create target of ID state. The
|
||||||
|
// collection of ATN states the following ID references includes
|
||||||
|
// states reached by traversing predicates. Since l is when we
|
||||||
|
// test them, we cannot cash the DFA state target of ID.
|
||||||
|
|
||||||
|
pt := trans.(*PredicateTransition)
|
||||||
|
|
||||||
|
if LexerATNSimulatorDebug {
|
||||||
|
fmt.Println("EVAL rule " + strconv.Itoa(trans.(*PredicateTransition).ruleIndex) + ":" + strconv.Itoa(pt.predIndex))
|
||||||
|
}
|
||||||
|
configs.SetHasSemanticContext(true)
|
||||||
|
if l.evaluatePredicate(input, pt.ruleIndex, pt.predIndex, speculative) {
|
||||||
|
cfg = NewLexerATNConfig4(config, trans.getTarget())
|
||||||
|
}
|
||||||
|
} else if trans.getSerializationType() == TransitionACTION {
|
||||||
|
if config.context == nil || config.context.hasEmptyPath() {
|
||||||
|
// execute actions anywhere in the start rule for a token.
|
||||||
|
//
|
||||||
|
// TODO: if the entry rule is invoked recursively, some
|
||||||
|
// actions may be executed during the recursive call. The
|
||||||
|
// problem can appear when hasEmptyPath() is true but
|
||||||
|
// isEmpty() is false. In l case, the config needs to be
|
||||||
|
// split into two contexts - one with just the empty path
|
||||||
|
// and another with everything but the empty path.
|
||||||
|
// Unfortunately, the current algorithm does not allow
|
||||||
|
// getEpsilonTarget to return two configurations, so
|
||||||
|
// additional modifications are needed before we can support
|
||||||
|
// the split operation.
|
||||||
|
lexerActionExecutor := LexerActionExecutorappend(config.lexerActionExecutor, l.atn.lexerActions[trans.(*ActionTransition).actionIndex])
|
||||||
|
cfg = NewLexerATNConfig3(config, trans.getTarget(), lexerActionExecutor)
|
||||||
|
} else {
|
||||||
|
// ignore actions in referenced rules
|
||||||
|
cfg = NewLexerATNConfig4(config, trans.getTarget())
|
||||||
|
}
|
||||||
|
} else if trans.getSerializationType() == TransitionEPSILON {
|
||||||
|
cfg = NewLexerATNConfig4(config, trans.getTarget())
|
||||||
|
} else if trans.getSerializationType() == TransitionATOM ||
|
||||||
|
trans.getSerializationType() == TransitionRANGE ||
|
||||||
|
trans.getSerializationType() == TransitionSET {
|
||||||
|
if treatEOFAsEpsilon {
|
||||||
|
if trans.Matches(TokenEOF, 0, LexerMaxCharValue) {
|
||||||
|
cfg = NewLexerATNConfig4(config, trans.getTarget())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
// Evaluate a predicate specified in the lexer.
|
||||||
|
//
|
||||||
|
// <p>If {@code speculative} is {@code true}, l method was called before
|
||||||
|
// {@link //consume} for the Matched character. This method should call
|
||||||
|
// {@link //consume} before evaluating the predicate to ensure position
|
||||||
|
// sensitive values, including {@link Lexer//GetText}, {@link Lexer//GetLine},
|
||||||
|
// and {@link Lexer//getcolumn}, properly reflect the current
|
||||||
|
// lexer state. This method should restore {@code input} and the simulator
|
||||||
|
// to the original state before returning (i.e. undo the actions made by the
|
||||||
|
// call to {@link //consume}.</p>
|
||||||
|
//
|
||||||
|
// @param input The input stream.
|
||||||
|
// @param ruleIndex The rule containing the predicate.
|
||||||
|
// @param predIndex The index of the predicate within the rule.
|
||||||
|
// @param speculative {@code true} if the current index in {@code input} is
|
||||||
|
// one character before the predicate's location.
|
||||||
|
//
|
||||||
|
// @return {@code true} if the specified predicate evaluates to
|
||||||
|
// {@code true}.
|
||||||
|
// /
|
||||||
|
func (l *LexerATNSimulator) evaluatePredicate(input CharStream, ruleIndex, predIndex int, speculative bool) bool {
|
||||||
|
// assume true if no recognizer was provided
|
||||||
|
if l.recog == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if !speculative {
|
||||||
|
return l.recog.Sempred(nil, ruleIndex, predIndex)
|
||||||
|
}
|
||||||
|
savedcolumn := l.CharPositionInLine
|
||||||
|
savedLine := l.Line
|
||||||
|
index := input.Index()
|
||||||
|
marker := input.Mark()
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
l.CharPositionInLine = savedcolumn
|
||||||
|
l.Line = savedLine
|
||||||
|
input.Seek(index)
|
||||||
|
input.Release(marker)
|
||||||
|
}()
|
||||||
|
|
||||||
|
l.Consume(input)
|
||||||
|
return l.recog.Sempred(nil, ruleIndex, predIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LexerATNSimulator) captureSimState(settings *SimState, input CharStream, dfaState *DFAState) {
|
||||||
|
settings.index = input.Index()
|
||||||
|
settings.line = l.Line
|
||||||
|
settings.column = l.CharPositionInLine
|
||||||
|
settings.dfaState = dfaState
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LexerATNSimulator) addDFAEdge(from *DFAState, tk int, to *DFAState, cfgs ATNConfigSet) *DFAState {
|
||||||
|
if to == nil && cfgs != nil {
|
||||||
|
// leading to l call, ATNConfigSet.hasSemanticContext is used as a
|
||||||
|
// marker indicating dynamic predicate evaluation makes l edge
|
||||||
|
// dependent on the specific input sequence, so the static edge in the
|
||||||
|
// DFA should be omitted. The target DFAState is still created since
|
||||||
|
// execATN has the ability to reSynchronize with the DFA state cache
|
||||||
|
// following the predicate evaluation step.
|
||||||
|
//
|
||||||
|
// TJP notes: next time through the DFA, we see a pred again and eval.
|
||||||
|
// If that gets us to a previously created (but dangling) DFA
|
||||||
|
// state, we can continue in pure DFA mode from there.
|
||||||
|
// /
|
||||||
|
suppressEdge := cfgs.HasSemanticContext()
|
||||||
|
cfgs.SetHasSemanticContext(false)
|
||||||
|
|
||||||
|
to = l.addDFAState(cfgs, true)
|
||||||
|
|
||||||
|
if suppressEdge {
|
||||||
|
return to
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// add the edge
|
||||||
|
if tk < LexerATNSimulatorMinDFAEdge || tk > LexerATNSimulatorMaxDFAEdge {
|
||||||
|
// Only track edges within the DFA bounds
|
||||||
|
return to
|
||||||
|
}
|
||||||
|
if LexerATNSimulatorDebug {
|
||||||
|
fmt.Println("EDGE " + from.String() + " -> " + to.String() + " upon " + strconv.Itoa(tk))
|
||||||
|
}
|
||||||
|
l.atn.edgeMu.Lock()
|
||||||
|
defer l.atn.edgeMu.Unlock()
|
||||||
|
if from.getEdges() == nil {
|
||||||
|
// make room for tokens 1..n and -1 masquerading as index 0
|
||||||
|
from.setEdges(make([]*DFAState, LexerATNSimulatorMaxDFAEdge-LexerATNSimulatorMinDFAEdge+1))
|
||||||
|
}
|
||||||
|
from.setIthEdge(tk-LexerATNSimulatorMinDFAEdge, to) // connect
|
||||||
|
|
||||||
|
return to
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a NewDFA state if there isn't one with l set of
|
||||||
|
// configurations already. This method also detects the first
|
||||||
|
// configuration containing an ATN rule stop state. Later, when
|
||||||
|
// traversing the DFA, we will know which rule to accept.
|
||||||
|
func (l *LexerATNSimulator) addDFAState(configs ATNConfigSet, suppressEdge bool) *DFAState {
|
||||||
|
|
||||||
|
proposed := NewDFAState(-1, configs)
|
||||||
|
var firstConfigWithRuleStopState ATNConfig
|
||||||
|
|
||||||
|
for _, cfg := range configs.GetItems() {
|
||||||
|
|
||||||
|
_, ok := cfg.GetState().(*RuleStopState)
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
firstConfigWithRuleStopState = cfg
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if firstConfigWithRuleStopState != nil {
|
||||||
|
proposed.isAcceptState = true
|
||||||
|
proposed.lexerActionExecutor = firstConfigWithRuleStopState.(*LexerATNConfig).lexerActionExecutor
|
||||||
|
proposed.setPrediction(l.atn.ruleToTokenType[firstConfigWithRuleStopState.GetState().GetRuleIndex()])
|
||||||
|
}
|
||||||
|
dfa := l.decisionToDFA[l.mode]
|
||||||
|
|
||||||
|
l.atn.stateMu.Lock()
|
||||||
|
defer l.atn.stateMu.Unlock()
|
||||||
|
existing, present := dfa.states.Get(proposed)
|
||||||
|
if present {
|
||||||
|
|
||||||
|
// This state was already present, so just return it.
|
||||||
|
//
|
||||||
|
proposed = existing
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// We need to add the new state
|
||||||
|
//
|
||||||
|
proposed.stateNumber = dfa.states.Len()
|
||||||
|
configs.SetReadOnly(true)
|
||||||
|
proposed.configs = configs
|
||||||
|
dfa.states.Put(proposed)
|
||||||
|
}
|
||||||
|
if !suppressEdge {
|
||||||
|
dfa.setS0(proposed)
|
||||||
|
}
|
||||||
|
return proposed
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LexerATNSimulator) getDFA(mode int) *DFA {
|
||||||
|
return l.decisionToDFA[mode]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the text Matched so far for the current token.
|
||||||
|
func (l *LexerATNSimulator) GetText(input CharStream) string {
|
||||||
|
// index is first lookahead char, don't include.
|
||||||
|
return input.GetTextFromInterval(NewInterval(l.startIndex, input.Index()-1))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LexerATNSimulator) Consume(input CharStream) {
|
||||||
|
curChar := input.LA(1)
|
||||||
|
if curChar == int('\n') {
|
||||||
|
l.Line++
|
||||||
|
l.CharPositionInLine = 0
|
||||||
|
} else {
|
||||||
|
l.CharPositionInLine++
|
||||||
|
}
|
||||||
|
input.Consume()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LexerATNSimulator) GetCharPositionInLine() int {
|
||||||
|
return l.CharPositionInLine
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LexerATNSimulator) GetLine() int {
|
||||||
|
return l.Line
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *LexerATNSimulator) GetTokenName(tt int) string {
|
||||||
|
if tt == -1 {
|
||||||
|
return "EOF"
|
||||||
|
}
|
||||||
|
|
||||||
|
var sb strings.Builder
|
||||||
|
sb.Grow(6)
|
||||||
|
sb.WriteByte('\'')
|
||||||
|
sb.WriteRune(rune(tt))
|
||||||
|
sb.WriteByte('\'')
|
||||||
|
|
||||||
|
return sb.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func resetSimState(sim *SimState) {
|
||||||
|
sim.index = -1
|
||||||
|
sim.line = 0
|
||||||
|
sim.column = -1
|
||||||
|
sim.dfaState = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type SimState struct {
|
||||||
|
index int
|
||||||
|
line int
|
||||||
|
column int
|
||||||
|
dfaState *DFAState
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSimState() *SimState {
|
||||||
|
s := new(SimState)
|
||||||
|
resetSimState(s)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SimState) reset() {
|
||||||
|
resetSimState(s)
|
||||||
|
}
|
||||||
216
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/ll1_analyzer.go
generated
vendored
Normal file
216
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/ll1_analyzer.go
generated
vendored
Normal file
@@ -0,0 +1,216 @@
|
|||||||
|
// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
||||||
|
// Use of this file is governed by the BSD 3-clause license that
|
||||||
|
// can be found in the LICENSE.txt file in the project root.
|
||||||
|
|
||||||
|
package antlr
|
||||||
|
|
||||||
|
type LL1Analyzer struct {
|
||||||
|
atn *ATN
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLL1Analyzer(atn *ATN) *LL1Analyzer {
|
||||||
|
la := new(LL1Analyzer)
|
||||||
|
la.atn = atn
|
||||||
|
return la
|
||||||
|
}
|
||||||
|
|
||||||
|
// - Special value added to the lookahead sets to indicate that we hit
|
||||||
|
// a predicate during analysis if {@code seeThruPreds==false}.
|
||||||
|
//
|
||||||
|
// /
|
||||||
|
const (
|
||||||
|
LL1AnalyzerHitPred = TokenInvalidType
|
||||||
|
)
|
||||||
|
|
||||||
|
// *
|
||||||
|
// Calculates the SLL(1) expected lookahead set for each outgoing transition
|
||||||
|
// of an {@link ATNState}. The returned array has one element for each
|
||||||
|
// outgoing transition in {@code s}. If the closure from transition
|
||||||
|
// <em>i</em> leads to a semantic predicate before Matching a symbol, the
|
||||||
|
// element at index <em>i</em> of the result will be {@code nil}.
|
||||||
|
//
|
||||||
|
// @param s the ATN state
|
||||||
|
// @return the expected symbols for each outgoing transition of {@code s}.
|
||||||
|
func (la *LL1Analyzer) getDecisionLookahead(s ATNState) []*IntervalSet {
|
||||||
|
if s == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
count := len(s.GetTransitions())
|
||||||
|
look := make([]*IntervalSet, count)
|
||||||
|
for alt := 0; alt < count; alt++ {
|
||||||
|
look[alt] = NewIntervalSet()
|
||||||
|
lookBusy := NewJStore[ATNConfig, Comparator[ATNConfig]](aConfEqInst)
|
||||||
|
seeThruPreds := false // fail to get lookahead upon pred
|
||||||
|
la.look1(s.GetTransitions()[alt].getTarget(), nil, BasePredictionContextEMPTY, look[alt], lookBusy, NewBitSet(), seeThruPreds, false)
|
||||||
|
// Wipe out lookahead for la alternative if we found nothing
|
||||||
|
// or we had a predicate when we !seeThruPreds
|
||||||
|
if look[alt].length() == 0 || look[alt].contains(LL1AnalyzerHitPred) {
|
||||||
|
look[alt] = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return look
|
||||||
|
}
|
||||||
|
|
||||||
|
// *
|
||||||
|
// Compute set of tokens that can follow {@code s} in the ATN in the
|
||||||
|
// specified {@code ctx}.
|
||||||
|
//
|
||||||
|
// <p>If {@code ctx} is {@code nil} and the end of the rule containing
|
||||||
|
// {@code s} is reached, {@link Token//EPSILON} is added to the result set.
|
||||||
|
// If {@code ctx} is not {@code nil} and the end of the outermost rule is
|
||||||
|
// reached, {@link Token//EOF} is added to the result set.</p>
|
||||||
|
//
|
||||||
|
// @param s the ATN state
|
||||||
|
// @param stopState the ATN state to stop at. This can be a
|
||||||
|
// {@link BlockEndState} to detect epsilon paths through a closure.
|
||||||
|
// @param ctx the complete parser context, or {@code nil} if the context
|
||||||
|
// should be ignored
|
||||||
|
//
|
||||||
|
// @return The set of tokens that can follow {@code s} in the ATN in the
|
||||||
|
// specified {@code ctx}.
|
||||||
|
// /
|
||||||
|
func (la *LL1Analyzer) Look(s, stopState ATNState, ctx RuleContext) *IntervalSet {
|
||||||
|
r := NewIntervalSet()
|
||||||
|
seeThruPreds := true // ignore preds get all lookahead
|
||||||
|
var lookContext PredictionContext
|
||||||
|
if ctx != nil {
|
||||||
|
lookContext = predictionContextFromRuleContext(s.GetATN(), ctx)
|
||||||
|
}
|
||||||
|
la.look1(s, stopState, lookContext, r, NewJStore[ATNConfig, Comparator[ATNConfig]](aConfEqInst), NewBitSet(), seeThruPreds, true)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
//*
|
||||||
|
// Compute set of tokens that can follow {@code s} in the ATN in the
|
||||||
|
// specified {@code ctx}.
|
||||||
|
//
|
||||||
|
// <p>If {@code ctx} is {@code nil} and {@code stopState} or the end of the
|
||||||
|
// rule containing {@code s} is reached, {@link Token//EPSILON} is added to
|
||||||
|
// the result set. If {@code ctx} is not {@code nil} and {@code addEOF} is
|
||||||
|
// {@code true} and {@code stopState} or the end of the outermost rule is
|
||||||
|
// reached, {@link Token//EOF} is added to the result set.</p>
|
||||||
|
//
|
||||||
|
// @param s the ATN state.
|
||||||
|
// @param stopState the ATN state to stop at. This can be a
|
||||||
|
// {@link BlockEndState} to detect epsilon paths through a closure.
|
||||||
|
// @param ctx The outer context, or {@code nil} if the outer context should
|
||||||
|
// not be used.
|
||||||
|
// @param look The result lookahead set.
|
||||||
|
// @param lookBusy A set used for preventing epsilon closures in the ATN
|
||||||
|
// from causing a stack overflow. Outside code should pass
|
||||||
|
// {@code NewSet<ATNConfig>} for la argument.
|
||||||
|
// @param calledRuleStack A set used for preventing left recursion in the
|
||||||
|
// ATN from causing a stack overflow. Outside code should pass
|
||||||
|
// {@code NewBitSet()} for la argument.
|
||||||
|
// @param seeThruPreds {@code true} to true semantic predicates as
|
||||||
|
// implicitly {@code true} and "see through them", otherwise {@code false}
|
||||||
|
// to treat semantic predicates as opaque and add {@link //HitPred} to the
|
||||||
|
// result if one is encountered.
|
||||||
|
// @param addEOF Add {@link Token//EOF} to the result if the end of the
|
||||||
|
// outermost context is reached. This parameter has no effect if {@code ctx}
|
||||||
|
// is {@code nil}.
|
||||||
|
|
||||||
|
func (la *LL1Analyzer) look2(s, stopState ATNState, ctx PredictionContext, look *IntervalSet, lookBusy *JStore[ATNConfig, Comparator[ATNConfig]], calledRuleStack *BitSet, seeThruPreds, addEOF bool, i int) {
|
||||||
|
|
||||||
|
returnState := la.atn.states[ctx.getReturnState(i)]
|
||||||
|
la.look1(returnState, stopState, ctx.GetParent(i), look, lookBusy, calledRuleStack, seeThruPreds, addEOF)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (la *LL1Analyzer) look1(s, stopState ATNState, ctx PredictionContext, look *IntervalSet, lookBusy *JStore[ATNConfig, Comparator[ATNConfig]], calledRuleStack *BitSet, seeThruPreds, addEOF bool) {
|
||||||
|
|
||||||
|
c := NewBaseATNConfig6(s, 0, ctx)
|
||||||
|
|
||||||
|
if lookBusy.Contains(c) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, present := lookBusy.Put(c)
|
||||||
|
if present {
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
if s == stopState {
|
||||||
|
if ctx == nil {
|
||||||
|
look.addOne(TokenEpsilon)
|
||||||
|
return
|
||||||
|
} else if ctx.isEmpty() && addEOF {
|
||||||
|
look.addOne(TokenEOF)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, ok := s.(*RuleStopState)
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
if ctx == nil {
|
||||||
|
look.addOne(TokenEpsilon)
|
||||||
|
return
|
||||||
|
} else if ctx.isEmpty() && addEOF {
|
||||||
|
look.addOne(TokenEOF)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx != BasePredictionContextEMPTY {
|
||||||
|
removed := calledRuleStack.contains(s.GetRuleIndex())
|
||||||
|
defer func() {
|
||||||
|
if removed {
|
||||||
|
calledRuleStack.add(s.GetRuleIndex())
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
calledRuleStack.remove(s.GetRuleIndex())
|
||||||
|
// run thru all possible stack tops in ctx
|
||||||
|
for i := 0; i < ctx.length(); i++ {
|
||||||
|
returnState := la.atn.states[ctx.getReturnState(i)]
|
||||||
|
la.look2(returnState, stopState, ctx, look, lookBusy, calledRuleStack, seeThruPreds, addEOF, i)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
n := len(s.GetTransitions())
|
||||||
|
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
t := s.GetTransitions()[i]
|
||||||
|
|
||||||
|
if t1, ok := t.(*RuleTransition); ok {
|
||||||
|
if calledRuleStack.contains(t1.getTarget().GetRuleIndex()) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
newContext := SingletonBasePredictionContextCreate(ctx, t1.followState.GetStateNumber())
|
||||||
|
la.look3(stopState, newContext, look, lookBusy, calledRuleStack, seeThruPreds, addEOF, t1)
|
||||||
|
} else if t2, ok := t.(AbstractPredicateTransition); ok {
|
||||||
|
if seeThruPreds {
|
||||||
|
la.look1(t2.getTarget(), stopState, ctx, look, lookBusy, calledRuleStack, seeThruPreds, addEOF)
|
||||||
|
} else {
|
||||||
|
look.addOne(LL1AnalyzerHitPred)
|
||||||
|
}
|
||||||
|
} else if t.getIsEpsilon() {
|
||||||
|
la.look1(t.getTarget(), stopState, ctx, look, lookBusy, calledRuleStack, seeThruPreds, addEOF)
|
||||||
|
} else if _, ok := t.(*WildcardTransition); ok {
|
||||||
|
look.addRange(TokenMinUserTokenType, la.atn.maxTokenType)
|
||||||
|
} else {
|
||||||
|
set := t.getLabel()
|
||||||
|
if set != nil {
|
||||||
|
if _, ok := t.(*NotSetTransition); ok {
|
||||||
|
set = set.complement(TokenMinUserTokenType, la.atn.maxTokenType)
|
||||||
|
}
|
||||||
|
look.addSet(set)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (la *LL1Analyzer) look3(stopState ATNState, ctx PredictionContext, look *IntervalSet, lookBusy *JStore[ATNConfig, Comparator[ATNConfig]], calledRuleStack *BitSet, seeThruPreds, addEOF bool, t1 *RuleTransition) {
|
||||||
|
|
||||||
|
newContext := SingletonBasePredictionContextCreate(ctx, t1.followState.GetStateNumber())
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
calledRuleStack.remove(t1.getTarget().GetRuleIndex())
|
||||||
|
}()
|
||||||
|
|
||||||
|
calledRuleStack.add(t1.getTarget().GetRuleIndex())
|
||||||
|
la.look1(t1.getTarget(), stopState, newContext, look, lookBusy, calledRuleStack, seeThruPreds, addEOF)
|
||||||
|
|
||||||
|
}
|
||||||
708
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/parser.go
generated
vendored
Normal file
708
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/parser.go
generated
vendored
Normal file
@@ -0,0 +1,708 @@
|
|||||||
|
// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
||||||
|
// Use of this file is governed by the BSD 3-clause license that
|
||||||
|
// can be found in the LICENSE.txt file in the project root.
|
||||||
|
|
||||||
|
package antlr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Parser interface {
|
||||||
|
Recognizer
|
||||||
|
|
||||||
|
GetInterpreter() *ParserATNSimulator
|
||||||
|
|
||||||
|
GetTokenStream() TokenStream
|
||||||
|
GetTokenFactory() TokenFactory
|
||||||
|
GetParserRuleContext() ParserRuleContext
|
||||||
|
SetParserRuleContext(ParserRuleContext)
|
||||||
|
Consume() Token
|
||||||
|
GetParseListeners() []ParseTreeListener
|
||||||
|
|
||||||
|
GetErrorHandler() ErrorStrategy
|
||||||
|
SetErrorHandler(ErrorStrategy)
|
||||||
|
GetInputStream() IntStream
|
||||||
|
GetCurrentToken() Token
|
||||||
|
GetExpectedTokens() *IntervalSet
|
||||||
|
NotifyErrorListeners(string, Token, RecognitionException)
|
||||||
|
IsExpectedToken(int) bool
|
||||||
|
GetPrecedence() int
|
||||||
|
GetRuleInvocationStack(ParserRuleContext) []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type BaseParser struct {
|
||||||
|
*BaseRecognizer
|
||||||
|
|
||||||
|
Interpreter *ParserATNSimulator
|
||||||
|
BuildParseTrees bool
|
||||||
|
|
||||||
|
input TokenStream
|
||||||
|
errHandler ErrorStrategy
|
||||||
|
precedenceStack IntStack
|
||||||
|
ctx ParserRuleContext
|
||||||
|
|
||||||
|
tracer *TraceListener
|
||||||
|
parseListeners []ParseTreeListener
|
||||||
|
_SyntaxErrors int
|
||||||
|
}
|
||||||
|
|
||||||
|
// p.is all the parsing support code essentially most of it is error
|
||||||
|
// recovery stuff.//
|
||||||
|
func NewBaseParser(input TokenStream) *BaseParser {
|
||||||
|
|
||||||
|
p := new(BaseParser)
|
||||||
|
|
||||||
|
p.BaseRecognizer = NewBaseRecognizer()
|
||||||
|
|
||||||
|
// The input stream.
|
||||||
|
p.input = nil
|
||||||
|
// The error handling strategy for the parser. The default value is a new
|
||||||
|
// instance of {@link DefaultErrorStrategy}.
|
||||||
|
p.errHandler = NewDefaultErrorStrategy()
|
||||||
|
p.precedenceStack = make([]int, 0)
|
||||||
|
p.precedenceStack.Push(0)
|
||||||
|
// The {@link ParserRuleContext} object for the currently executing rule.
|
||||||
|
// p.is always non-nil during the parsing process.
|
||||||
|
p.ctx = nil
|
||||||
|
// Specifies whether or not the parser should construct a parse tree during
|
||||||
|
// the parsing process. The default value is {@code true}.
|
||||||
|
p.BuildParseTrees = true
|
||||||
|
// When {@link //setTrace}{@code (true)} is called, a reference to the
|
||||||
|
// {@link TraceListener} is stored here so it can be easily removed in a
|
||||||
|
// later call to {@link //setTrace}{@code (false)}. The listener itself is
|
||||||
|
// implemented as a parser listener so p.field is not directly used by
|
||||||
|
// other parser methods.
|
||||||
|
p.tracer = nil
|
||||||
|
// The list of {@link ParseTreeListener} listeners registered to receive
|
||||||
|
// events during the parse.
|
||||||
|
p.parseListeners = nil
|
||||||
|
// The number of syntax errors Reported during parsing. p.value is
|
||||||
|
// incremented each time {@link //NotifyErrorListeners} is called.
|
||||||
|
p._SyntaxErrors = 0
|
||||||
|
p.SetInputStream(input)
|
||||||
|
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
// p.field maps from the serialized ATN string to the deserialized {@link
|
||||||
|
// ATN} with
|
||||||
|
// bypass alternatives.
|
||||||
|
//
|
||||||
|
// @see ATNDeserializationOptions//isGenerateRuleBypassTransitions()
|
||||||
|
var bypassAltsAtnCache = make(map[string]int)
|
||||||
|
|
||||||
|
// reset the parser's state//
|
||||||
|
func (p *BaseParser) reset() {
|
||||||
|
if p.input != nil {
|
||||||
|
p.input.Seek(0)
|
||||||
|
}
|
||||||
|
p.errHandler.reset(p)
|
||||||
|
p.ctx = nil
|
||||||
|
p._SyntaxErrors = 0
|
||||||
|
p.SetTrace(nil)
|
||||||
|
p.precedenceStack = make([]int, 0)
|
||||||
|
p.precedenceStack.Push(0)
|
||||||
|
if p.Interpreter != nil {
|
||||||
|
p.Interpreter.reset()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *BaseParser) GetErrorHandler() ErrorStrategy {
|
||||||
|
return p.errHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *BaseParser) SetErrorHandler(e ErrorStrategy) {
|
||||||
|
p.errHandler = e
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match current input symbol against {@code ttype}. If the symbol type
|
||||||
|
// Matches, {@link ANTLRErrorStrategy//ReportMatch} and {@link //consume} are
|
||||||
|
// called to complete the Match process.
|
||||||
|
//
|
||||||
|
// <p>If the symbol type does not Match,
|
||||||
|
// {@link ANTLRErrorStrategy//recoverInline} is called on the current error
|
||||||
|
// strategy to attempt recovery. If {@link //getBuildParseTree} is
|
||||||
|
// {@code true} and the token index of the symbol returned by
|
||||||
|
// {@link ANTLRErrorStrategy//recoverInline} is -1, the symbol is added to
|
||||||
|
// the parse tree by calling {@link ParserRuleContext//addErrorNode}.</p>
|
||||||
|
//
|
||||||
|
// @param ttype the token type to Match
|
||||||
|
// @return the Matched symbol
|
||||||
|
// @panics RecognitionException if the current input symbol did not Match
|
||||||
|
// {@code ttype} and the error strategy could not recover from the
|
||||||
|
// mismatched symbol
|
||||||
|
|
||||||
|
func (p *BaseParser) Match(ttype int) Token {
|
||||||
|
|
||||||
|
t := p.GetCurrentToken()
|
||||||
|
|
||||||
|
if t.GetTokenType() == ttype {
|
||||||
|
p.errHandler.ReportMatch(p)
|
||||||
|
p.Consume()
|
||||||
|
} else {
|
||||||
|
t = p.errHandler.RecoverInline(p)
|
||||||
|
if p.BuildParseTrees && t.GetTokenIndex() == -1 {
|
||||||
|
// we must have conjured up a Newtoken during single token
|
||||||
|
// insertion
|
||||||
|
// if it's not the current symbol
|
||||||
|
p.ctx.AddErrorNode(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match current input symbol as a wildcard. If the symbol type Matches
|
||||||
|
// (i.e. has a value greater than 0), {@link ANTLRErrorStrategy//ReportMatch}
|
||||||
|
// and {@link //consume} are called to complete the Match process.
|
||||||
|
//
|
||||||
|
// <p>If the symbol type does not Match,
|
||||||
|
// {@link ANTLRErrorStrategy//recoverInline} is called on the current error
|
||||||
|
// strategy to attempt recovery. If {@link //getBuildParseTree} is
|
||||||
|
// {@code true} and the token index of the symbol returned by
|
||||||
|
// {@link ANTLRErrorStrategy//recoverInline} is -1, the symbol is added to
|
||||||
|
// the parse tree by calling {@link ParserRuleContext//addErrorNode}.</p>
|
||||||
|
//
|
||||||
|
// @return the Matched symbol
|
||||||
|
// @panics RecognitionException if the current input symbol did not Match
|
||||||
|
// a wildcard and the error strategy could not recover from the mismatched
|
||||||
|
// symbol
|
||||||
|
|
||||||
|
func (p *BaseParser) MatchWildcard() Token {
|
||||||
|
t := p.GetCurrentToken()
|
||||||
|
if t.GetTokenType() > 0 {
|
||||||
|
p.errHandler.ReportMatch(p)
|
||||||
|
p.Consume()
|
||||||
|
} else {
|
||||||
|
t = p.errHandler.RecoverInline(p)
|
||||||
|
if p.BuildParseTrees && t.GetTokenIndex() == -1 {
|
||||||
|
// we must have conjured up a Newtoken during single token
|
||||||
|
// insertion
|
||||||
|
// if it's not the current symbol
|
||||||
|
p.ctx.AddErrorNode(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *BaseParser) GetParserRuleContext() ParserRuleContext {
|
||||||
|
return p.ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *BaseParser) SetParserRuleContext(v ParserRuleContext) {
|
||||||
|
p.ctx = v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *BaseParser) GetParseListeners() []ParseTreeListener {
|
||||||
|
if p.parseListeners == nil {
|
||||||
|
return make([]ParseTreeListener, 0)
|
||||||
|
}
|
||||||
|
return p.parseListeners
|
||||||
|
}
|
||||||
|
|
||||||
|
// Registers {@code listener} to receive events during the parsing process.
|
||||||
|
//
|
||||||
|
// <p>To support output-preserving grammar transformations (including but not
|
||||||
|
// limited to left-recursion removal, automated left-factoring, and
|
||||||
|
// optimized code generation), calls to listener methods during the parse
|
||||||
|
// may differ substantially from calls made by
|
||||||
|
// {@link ParseTreeWalker//DEFAULT} used after the parse is complete. In
|
||||||
|
// particular, rule entry and exit events may occur in a different order
|
||||||
|
// during the parse than after the parser. In addition, calls to certain
|
||||||
|
// rule entry methods may be omitted.</p>
|
||||||
|
//
|
||||||
|
// <p>With the following specific exceptions, calls to listener events are
|
||||||
|
// <em>deterministic</em>, i.e. for identical input the calls to listener
|
||||||
|
// methods will be the same.</p>
|
||||||
|
//
|
||||||
|
// <ul>
|
||||||
|
// <li>Alterations to the grammar used to generate code may change the
|
||||||
|
// behavior of the listener calls.</li>
|
||||||
|
// <li>Alterations to the command line options passed to ANTLR 4 when
|
||||||
|
// generating the parser may change the behavior of the listener calls.</li>
|
||||||
|
// <li>Changing the version of the ANTLR Tool used to generate the parser
|
||||||
|
// may change the behavior of the listener calls.</li>
|
||||||
|
// </ul>
|
||||||
|
//
|
||||||
|
// @param listener the listener to add
|
||||||
|
//
|
||||||
|
// @panics nilPointerException if {@code} listener is {@code nil}
|
||||||
|
func (p *BaseParser) AddParseListener(listener ParseTreeListener) {
|
||||||
|
if listener == nil {
|
||||||
|
panic("listener")
|
||||||
|
}
|
||||||
|
if p.parseListeners == nil {
|
||||||
|
p.parseListeners = make([]ParseTreeListener, 0)
|
||||||
|
}
|
||||||
|
p.parseListeners = append(p.parseListeners, listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove {@code listener} from the list of parse listeners.
|
||||||
|
//
|
||||||
|
// <p>If {@code listener} is {@code nil} or has not been added as a parse
|
||||||
|
// listener, p.method does nothing.</p>
|
||||||
|
// @param listener the listener to remove
|
||||||
|
func (p *BaseParser) RemoveParseListener(listener ParseTreeListener) {
|
||||||
|
|
||||||
|
if p.parseListeners != nil {
|
||||||
|
|
||||||
|
idx := -1
|
||||||
|
for i, v := range p.parseListeners {
|
||||||
|
if v == listener {
|
||||||
|
idx = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if idx == -1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove the listener from the slice
|
||||||
|
p.parseListeners = append(p.parseListeners[0:idx], p.parseListeners[idx+1:]...)
|
||||||
|
|
||||||
|
if len(p.parseListeners) == 0 {
|
||||||
|
p.parseListeners = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove all parse listeners.
|
||||||
|
func (p *BaseParser) removeParseListeners() {
|
||||||
|
p.parseListeners = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify any parse listeners of an enter rule event.
|
||||||
|
func (p *BaseParser) TriggerEnterRuleEvent() {
|
||||||
|
if p.parseListeners != nil {
|
||||||
|
ctx := p.ctx
|
||||||
|
for _, listener := range p.parseListeners {
|
||||||
|
listener.EnterEveryRule(ctx)
|
||||||
|
ctx.EnterRule(listener)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify any parse listeners of an exit rule event.
|
||||||
|
//
|
||||||
|
// @see //addParseListener
|
||||||
|
func (p *BaseParser) TriggerExitRuleEvent() {
|
||||||
|
if p.parseListeners != nil {
|
||||||
|
// reverse order walk of listeners
|
||||||
|
ctx := p.ctx
|
||||||
|
l := len(p.parseListeners) - 1
|
||||||
|
|
||||||
|
for i := range p.parseListeners {
|
||||||
|
listener := p.parseListeners[l-i]
|
||||||
|
ctx.ExitRule(listener)
|
||||||
|
listener.ExitEveryRule(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *BaseParser) GetInterpreter() *ParserATNSimulator {
|
||||||
|
return p.Interpreter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *BaseParser) GetATN() *ATN {
|
||||||
|
return p.Interpreter.atn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *BaseParser) GetTokenFactory() TokenFactory {
|
||||||
|
return p.input.GetTokenSource().GetTokenFactory()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tell our token source and error strategy about a Newway to create tokens.//
|
||||||
|
func (p *BaseParser) setTokenFactory(factory TokenFactory) {
|
||||||
|
p.input.GetTokenSource().setTokenFactory(factory)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The ATN with bypass alternatives is expensive to create so we create it
|
||||||
|
// lazily.
|
||||||
|
//
|
||||||
|
// @panics UnsupportedOperationException if the current parser does not
|
||||||
|
// implement the {@link //getSerializedATN()} method.
|
||||||
|
func (p *BaseParser) GetATNWithBypassAlts() {
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
panic("Not implemented!")
|
||||||
|
|
||||||
|
// serializedAtn := p.getSerializedATN()
|
||||||
|
// if (serializedAtn == nil) {
|
||||||
|
// panic("The current parser does not support an ATN with bypass alternatives.")
|
||||||
|
// }
|
||||||
|
// result := p.bypassAltsAtnCache[serializedAtn]
|
||||||
|
// if (result == nil) {
|
||||||
|
// deserializationOptions := NewATNDeserializationOptions(nil)
|
||||||
|
// deserializationOptions.generateRuleBypassTransitions = true
|
||||||
|
// result = NewATNDeserializer(deserializationOptions).deserialize(serializedAtn)
|
||||||
|
// p.bypassAltsAtnCache[serializedAtn] = result
|
||||||
|
// }
|
||||||
|
// return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// The preferred method of getting a tree pattern. For example, here's a
|
||||||
|
// sample use:
|
||||||
|
//
|
||||||
|
// <pre>
|
||||||
|
// ParseTree t = parser.expr()
|
||||||
|
// ParseTreePattern p = parser.compileParseTreePattern("<ID>+0",
|
||||||
|
// MyParser.RULE_expr)
|
||||||
|
// ParseTreeMatch m = p.Match(t)
|
||||||
|
// String id = m.Get("ID")
|
||||||
|
// </pre>
|
||||||
|
|
||||||
|
func (p *BaseParser) compileParseTreePattern(pattern, patternRuleIndex, lexer Lexer) {
|
||||||
|
|
||||||
|
panic("NewParseTreePatternMatcher not implemented!")
|
||||||
|
//
|
||||||
|
// if (lexer == nil) {
|
||||||
|
// if (p.GetTokenStream() != nil) {
|
||||||
|
// tokenSource := p.GetTokenStream().GetTokenSource()
|
||||||
|
// if _, ok := tokenSource.(ILexer); ok {
|
||||||
|
// lexer = tokenSource
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// if (lexer == nil) {
|
||||||
|
// panic("Parser can't discover a lexer to use")
|
||||||
|
// }
|
||||||
|
|
||||||
|
// m := NewParseTreePatternMatcher(lexer, p)
|
||||||
|
// return m.compile(pattern, patternRuleIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *BaseParser) GetInputStream() IntStream {
|
||||||
|
return p.GetTokenStream()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *BaseParser) SetInputStream(input TokenStream) {
|
||||||
|
p.SetTokenStream(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *BaseParser) GetTokenStream() TokenStream {
|
||||||
|
return p.input
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the token stream and reset the parser.//
|
||||||
|
func (p *BaseParser) SetTokenStream(input TokenStream) {
|
||||||
|
p.input = nil
|
||||||
|
p.reset()
|
||||||
|
p.input = input
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match needs to return the current input symbol, which gets put
|
||||||
|
// into the label for the associated token ref e.g., x=ID.
|
||||||
|
func (p *BaseParser) GetCurrentToken() Token {
|
||||||
|
return p.input.LT(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *BaseParser) NotifyErrorListeners(msg string, offendingToken Token, err RecognitionException) {
|
||||||
|
if offendingToken == nil {
|
||||||
|
offendingToken = p.GetCurrentToken()
|
||||||
|
}
|
||||||
|
p._SyntaxErrors++
|
||||||
|
line := offendingToken.GetLine()
|
||||||
|
column := offendingToken.GetColumn()
|
||||||
|
listener := p.GetErrorListenerDispatch()
|
||||||
|
listener.SyntaxError(p, offendingToken, line, column, msg, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *BaseParser) Consume() Token {
|
||||||
|
o := p.GetCurrentToken()
|
||||||
|
if o.GetTokenType() != TokenEOF {
|
||||||
|
p.GetInputStream().Consume()
|
||||||
|
}
|
||||||
|
hasListener := p.parseListeners != nil && len(p.parseListeners) > 0
|
||||||
|
if p.BuildParseTrees || hasListener {
|
||||||
|
if p.errHandler.InErrorRecoveryMode(p) {
|
||||||
|
node := p.ctx.AddErrorNode(o)
|
||||||
|
if p.parseListeners != nil {
|
||||||
|
for _, l := range p.parseListeners {
|
||||||
|
l.VisitErrorNode(node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
node := p.ctx.AddTokenNode(o)
|
||||||
|
if p.parseListeners != nil {
|
||||||
|
for _, l := range p.parseListeners {
|
||||||
|
l.VisitTerminal(node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// node.invokingState = p.state
|
||||||
|
}
|
||||||
|
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *BaseParser) addContextToParseTree() {
|
||||||
|
// add current context to parent if we have a parent
|
||||||
|
if p.ctx.GetParent() != nil {
|
||||||
|
p.ctx.GetParent().(ParserRuleContext).AddChild(p.ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *BaseParser) EnterRule(localctx ParserRuleContext, state, ruleIndex int) {
|
||||||
|
p.SetState(state)
|
||||||
|
p.ctx = localctx
|
||||||
|
p.ctx.SetStart(p.input.LT(1))
|
||||||
|
if p.BuildParseTrees {
|
||||||
|
p.addContextToParseTree()
|
||||||
|
}
|
||||||
|
if p.parseListeners != nil {
|
||||||
|
p.TriggerEnterRuleEvent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *BaseParser) ExitRule() {
|
||||||
|
p.ctx.SetStop(p.input.LT(-1))
|
||||||
|
// trigger event on ctx, before it reverts to parent
|
||||||
|
if p.parseListeners != nil {
|
||||||
|
p.TriggerExitRuleEvent()
|
||||||
|
}
|
||||||
|
p.SetState(p.ctx.GetInvokingState())
|
||||||
|
if p.ctx.GetParent() != nil {
|
||||||
|
p.ctx = p.ctx.GetParent().(ParserRuleContext)
|
||||||
|
} else {
|
||||||
|
p.ctx = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *BaseParser) EnterOuterAlt(localctx ParserRuleContext, altNum int) {
|
||||||
|
localctx.SetAltNumber(altNum)
|
||||||
|
// if we have Newlocalctx, make sure we replace existing ctx
|
||||||
|
// that is previous child of parse tree
|
||||||
|
if p.BuildParseTrees && p.ctx != localctx {
|
||||||
|
if p.ctx.GetParent() != nil {
|
||||||
|
p.ctx.GetParent().(ParserRuleContext).RemoveLastChild()
|
||||||
|
p.ctx.GetParent().(ParserRuleContext).AddChild(localctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.ctx = localctx
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the precedence level for the top-most precedence rule.
|
||||||
|
//
|
||||||
|
// @return The precedence level for the top-most precedence rule, or -1 if
|
||||||
|
// the parser context is not nested within a precedence rule.
|
||||||
|
|
||||||
|
func (p *BaseParser) GetPrecedence() int {
|
||||||
|
if len(p.precedenceStack) == 0 {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.precedenceStack[len(p.precedenceStack)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *BaseParser) EnterRecursionRule(localctx ParserRuleContext, state, ruleIndex, precedence int) {
|
||||||
|
p.SetState(state)
|
||||||
|
p.precedenceStack.Push(precedence)
|
||||||
|
p.ctx = localctx
|
||||||
|
p.ctx.SetStart(p.input.LT(1))
|
||||||
|
if p.parseListeners != nil {
|
||||||
|
p.TriggerEnterRuleEvent() // simulates rule entry for
|
||||||
|
// left-recursive rules
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Like {@link //EnterRule} but for recursive rules.
|
||||||
|
|
||||||
|
func (p *BaseParser) PushNewRecursionContext(localctx ParserRuleContext, state, ruleIndex int) {
|
||||||
|
previous := p.ctx
|
||||||
|
previous.SetParent(localctx)
|
||||||
|
previous.SetInvokingState(state)
|
||||||
|
previous.SetStop(p.input.LT(-1))
|
||||||
|
|
||||||
|
p.ctx = localctx
|
||||||
|
p.ctx.SetStart(previous.GetStart())
|
||||||
|
if p.BuildParseTrees {
|
||||||
|
p.ctx.AddChild(previous)
|
||||||
|
}
|
||||||
|
if p.parseListeners != nil {
|
||||||
|
p.TriggerEnterRuleEvent() // simulates rule entry for
|
||||||
|
// left-recursive rules
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *BaseParser) UnrollRecursionContexts(parentCtx ParserRuleContext) {
|
||||||
|
p.precedenceStack.Pop()
|
||||||
|
p.ctx.SetStop(p.input.LT(-1))
|
||||||
|
retCtx := p.ctx // save current ctx (return value)
|
||||||
|
// unroll so ctx is as it was before call to recursive method
|
||||||
|
if p.parseListeners != nil {
|
||||||
|
for p.ctx != parentCtx {
|
||||||
|
p.TriggerExitRuleEvent()
|
||||||
|
p.ctx = p.ctx.GetParent().(ParserRuleContext)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
p.ctx = parentCtx
|
||||||
|
}
|
||||||
|
// hook into tree
|
||||||
|
retCtx.SetParent(parentCtx)
|
||||||
|
if p.BuildParseTrees && parentCtx != nil {
|
||||||
|
// add return ctx into invoking rule's tree
|
||||||
|
parentCtx.AddChild(retCtx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *BaseParser) GetInvokingContext(ruleIndex int) ParserRuleContext {
|
||||||
|
ctx := p.ctx
|
||||||
|
for ctx != nil {
|
||||||
|
if ctx.GetRuleIndex() == ruleIndex {
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
ctx = ctx.GetParent().(ParserRuleContext)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *BaseParser) Precpred(localctx RuleContext, precedence int) bool {
|
||||||
|
return precedence >= p.precedenceStack[len(p.precedenceStack)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *BaseParser) inContext(context ParserRuleContext) bool {
|
||||||
|
// TODO: useful in parser?
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Checks whether or not {@code symbol} can follow the current state in the
|
||||||
|
// ATN. The behavior of p.method is equivalent to the following, but is
|
||||||
|
// implemented such that the complete context-sensitive follow set does not
|
||||||
|
// need to be explicitly constructed.
|
||||||
|
//
|
||||||
|
// <pre>
|
||||||
|
// return getExpectedTokens().contains(symbol)
|
||||||
|
// </pre>
|
||||||
|
//
|
||||||
|
// @param symbol the symbol type to check
|
||||||
|
// @return {@code true} if {@code symbol} can follow the current state in
|
||||||
|
// the ATN, otherwise {@code false}.
|
||||||
|
|
||||||
|
func (p *BaseParser) IsExpectedToken(symbol int) bool {
|
||||||
|
atn := p.Interpreter.atn
|
||||||
|
ctx := p.ctx
|
||||||
|
s := atn.states[p.state]
|
||||||
|
following := atn.NextTokens(s, nil)
|
||||||
|
if following.contains(symbol) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if !following.contains(TokenEpsilon) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for ctx != nil && ctx.GetInvokingState() >= 0 && following.contains(TokenEpsilon) {
|
||||||
|
invokingState := atn.states[ctx.GetInvokingState()]
|
||||||
|
rt := invokingState.GetTransitions()[0]
|
||||||
|
following = atn.NextTokens(rt.(*RuleTransition).followState, nil)
|
||||||
|
if following.contains(symbol) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
ctx = ctx.GetParent().(ParserRuleContext)
|
||||||
|
}
|
||||||
|
if following.contains(TokenEpsilon) && symbol == TokenEOF {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Computes the set of input symbols which could follow the current parser
|
||||||
|
// state and context, as given by {@link //GetState} and {@link //GetContext},
|
||||||
|
// respectively.
|
||||||
|
//
|
||||||
|
// @see ATN//getExpectedTokens(int, RuleContext)
|
||||||
|
func (p *BaseParser) GetExpectedTokens() *IntervalSet {
|
||||||
|
return p.Interpreter.atn.getExpectedTokens(p.state, p.ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *BaseParser) GetExpectedTokensWithinCurrentRule() *IntervalSet {
|
||||||
|
atn := p.Interpreter.atn
|
||||||
|
s := atn.states[p.state]
|
||||||
|
return atn.NextTokens(s, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a rule's index (i.e., {@code RULE_ruleName} field) or -1 if not found.//
|
||||||
|
func (p *BaseParser) GetRuleIndex(ruleName string) int {
|
||||||
|
var ruleIndex, ok = p.GetRuleIndexMap()[ruleName]
|
||||||
|
if ok {
|
||||||
|
return ruleIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return List<String> of the rule names in your parser instance
|
||||||
|
// leading up to a call to the current rule. You could override if
|
||||||
|
// you want more details such as the file/line info of where
|
||||||
|
// in the ATN a rule is invoked.
|
||||||
|
//
|
||||||
|
// this very useful for error messages.
|
||||||
|
|
||||||
|
func (p *BaseParser) GetRuleInvocationStack(c ParserRuleContext) []string {
|
||||||
|
if c == nil {
|
||||||
|
c = p.ctx
|
||||||
|
}
|
||||||
|
stack := make([]string, 0)
|
||||||
|
for c != nil {
|
||||||
|
// compute what follows who invoked us
|
||||||
|
ruleIndex := c.GetRuleIndex()
|
||||||
|
if ruleIndex < 0 {
|
||||||
|
stack = append(stack, "n/a")
|
||||||
|
} else {
|
||||||
|
stack = append(stack, p.GetRuleNames()[ruleIndex])
|
||||||
|
}
|
||||||
|
|
||||||
|
vp := c.GetParent()
|
||||||
|
|
||||||
|
if vp == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
c = vp.(ParserRuleContext)
|
||||||
|
}
|
||||||
|
return stack
|
||||||
|
}
|
||||||
|
|
||||||
|
// For debugging and other purposes.//
|
||||||
|
func (p *BaseParser) GetDFAStrings() string {
|
||||||
|
return fmt.Sprint(p.Interpreter.decisionToDFA)
|
||||||
|
}
|
||||||
|
|
||||||
|
// For debugging and other purposes.//
|
||||||
|
func (p *BaseParser) DumpDFA() {
|
||||||
|
seenOne := false
|
||||||
|
for _, dfa := range p.Interpreter.decisionToDFA {
|
||||||
|
if dfa.states.Len() > 0 {
|
||||||
|
if seenOne {
|
||||||
|
fmt.Println()
|
||||||
|
}
|
||||||
|
fmt.Println("Decision " + strconv.Itoa(dfa.decision) + ":")
|
||||||
|
fmt.Print(dfa.String(p.LiteralNames, p.SymbolicNames))
|
||||||
|
seenOne = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *BaseParser) GetSourceName() string {
|
||||||
|
return p.GrammarFileName
|
||||||
|
}
|
||||||
|
|
||||||
|
// During a parse is sometimes useful to listen in on the rule entry and exit
|
||||||
|
// events as well as token Matches. p.is for quick and dirty debugging.
|
||||||
|
func (p *BaseParser) SetTrace(trace *TraceListener) {
|
||||||
|
if trace == nil {
|
||||||
|
p.RemoveParseListener(p.tracer)
|
||||||
|
p.tracer = nil
|
||||||
|
} else {
|
||||||
|
if p.tracer != nil {
|
||||||
|
p.RemoveParseListener(p.tracer)
|
||||||
|
}
|
||||||
|
p.tracer = NewTraceListener(p)
|
||||||
|
p.AddParseListener(p.tracer)
|
||||||
|
}
|
||||||
|
}
|
||||||
1559
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/parser_atn_simulator.go
generated
vendored
Normal file
1559
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/parser_atn_simulator.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
362
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/parser_rule_context.go
generated
vendored
Normal file
362
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/parser_rule_context.go
generated
vendored
Normal file
@@ -0,0 +1,362 @@
|
|||||||
|
// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
||||||
|
// Use of this file is governed by the BSD 3-clause license that
|
||||||
|
// can be found in the LICENSE.txt file in the project root.
|
||||||
|
|
||||||
|
package antlr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ParserRuleContext interface {
|
||||||
|
RuleContext
|
||||||
|
|
||||||
|
SetException(RecognitionException)
|
||||||
|
|
||||||
|
AddTokenNode(token Token) *TerminalNodeImpl
|
||||||
|
AddErrorNode(badToken Token) *ErrorNodeImpl
|
||||||
|
|
||||||
|
EnterRule(listener ParseTreeListener)
|
||||||
|
ExitRule(listener ParseTreeListener)
|
||||||
|
|
||||||
|
SetStart(Token)
|
||||||
|
GetStart() Token
|
||||||
|
|
||||||
|
SetStop(Token)
|
||||||
|
GetStop() Token
|
||||||
|
|
||||||
|
AddChild(child RuleContext) RuleContext
|
||||||
|
RemoveLastChild()
|
||||||
|
}
|
||||||
|
|
||||||
|
type BaseParserRuleContext struct {
|
||||||
|
*BaseRuleContext
|
||||||
|
|
||||||
|
start, stop Token
|
||||||
|
exception RecognitionException
|
||||||
|
children []Tree
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBaseParserRuleContext(parent ParserRuleContext, invokingStateNumber int) *BaseParserRuleContext {
|
||||||
|
prc := new(BaseParserRuleContext)
|
||||||
|
|
||||||
|
prc.BaseRuleContext = NewBaseRuleContext(parent, invokingStateNumber)
|
||||||
|
|
||||||
|
prc.RuleIndex = -1
|
||||||
|
// * If we are debugging or building a parse tree for a Visitor,
|
||||||
|
// we need to track all of the tokens and rule invocations associated
|
||||||
|
// with prc rule's context. This is empty for parsing w/o tree constr.
|
||||||
|
// operation because we don't the need to track the details about
|
||||||
|
// how we parse prc rule.
|
||||||
|
// /
|
||||||
|
prc.children = nil
|
||||||
|
prc.start = nil
|
||||||
|
prc.stop = nil
|
||||||
|
// The exception that forced prc rule to return. If the rule successfully
|
||||||
|
// completed, prc is {@code nil}.
|
||||||
|
prc.exception = nil
|
||||||
|
|
||||||
|
return prc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (prc *BaseParserRuleContext) SetException(e RecognitionException) {
|
||||||
|
prc.exception = e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (prc *BaseParserRuleContext) GetChildren() []Tree {
|
||||||
|
return prc.children
|
||||||
|
}
|
||||||
|
|
||||||
|
func (prc *BaseParserRuleContext) CopyFrom(ctx *BaseParserRuleContext) {
|
||||||
|
// from RuleContext
|
||||||
|
prc.parentCtx = ctx.parentCtx
|
||||||
|
prc.invokingState = ctx.invokingState
|
||||||
|
prc.children = nil
|
||||||
|
prc.start = ctx.start
|
||||||
|
prc.stop = ctx.stop
|
||||||
|
}
|
||||||
|
|
||||||
|
func (prc *BaseParserRuleContext) GetText() string {
|
||||||
|
if prc.GetChildCount() == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var s string
|
||||||
|
for _, child := range prc.children {
|
||||||
|
s += child.(ParseTree).GetText()
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// Double dispatch methods for listeners
|
||||||
|
func (prc *BaseParserRuleContext) EnterRule(listener ParseTreeListener) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (prc *BaseParserRuleContext) ExitRule(listener ParseTreeListener) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// * Does not set parent link other add methods do that///
|
||||||
|
func (prc *BaseParserRuleContext) addTerminalNodeChild(child TerminalNode) TerminalNode {
|
||||||
|
if prc.children == nil {
|
||||||
|
prc.children = make([]Tree, 0)
|
||||||
|
}
|
||||||
|
if child == nil {
|
||||||
|
panic("Child may not be null")
|
||||||
|
}
|
||||||
|
prc.children = append(prc.children, child)
|
||||||
|
return child
|
||||||
|
}
|
||||||
|
|
||||||
|
func (prc *BaseParserRuleContext) AddChild(child RuleContext) RuleContext {
|
||||||
|
if prc.children == nil {
|
||||||
|
prc.children = make([]Tree, 0)
|
||||||
|
}
|
||||||
|
if child == nil {
|
||||||
|
panic("Child may not be null")
|
||||||
|
}
|
||||||
|
prc.children = append(prc.children, child)
|
||||||
|
return child
|
||||||
|
}
|
||||||
|
|
||||||
|
// * Used by EnterOuterAlt to toss out a RuleContext previously added as
|
||||||
|
// we entered a rule. If we have // label, we will need to remove
|
||||||
|
// generic ruleContext object.
|
||||||
|
// /
|
||||||
|
func (prc *BaseParserRuleContext) RemoveLastChild() {
|
||||||
|
if prc.children != nil && len(prc.children) > 0 {
|
||||||
|
prc.children = prc.children[0 : len(prc.children)-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (prc *BaseParserRuleContext) AddTokenNode(token Token) *TerminalNodeImpl {
|
||||||
|
|
||||||
|
node := NewTerminalNodeImpl(token)
|
||||||
|
prc.addTerminalNodeChild(node)
|
||||||
|
node.parentCtx = prc
|
||||||
|
return node
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (prc *BaseParserRuleContext) AddErrorNode(badToken Token) *ErrorNodeImpl {
|
||||||
|
node := NewErrorNodeImpl(badToken)
|
||||||
|
prc.addTerminalNodeChild(node)
|
||||||
|
node.parentCtx = prc
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
func (prc *BaseParserRuleContext) GetChild(i int) Tree {
|
||||||
|
if prc.children != nil && len(prc.children) >= i {
|
||||||
|
return prc.children[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (prc *BaseParserRuleContext) GetChildOfType(i int, childType reflect.Type) RuleContext {
|
||||||
|
if childType == nil {
|
||||||
|
return prc.GetChild(i).(RuleContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
for j := 0; j < len(prc.children); j++ {
|
||||||
|
child := prc.children[j]
|
||||||
|
if reflect.TypeOf(child) == childType {
|
||||||
|
if i == 0 {
|
||||||
|
return child.(RuleContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
i--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (prc *BaseParserRuleContext) ToStringTree(ruleNames []string, recog Recognizer) string {
|
||||||
|
return TreesStringTree(prc, ruleNames, recog)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (prc *BaseParserRuleContext) GetRuleContext() RuleContext {
|
||||||
|
return prc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (prc *BaseParserRuleContext) Accept(visitor ParseTreeVisitor) interface{} {
|
||||||
|
return visitor.VisitChildren(prc)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (prc *BaseParserRuleContext) SetStart(t Token) {
|
||||||
|
prc.start = t
|
||||||
|
}
|
||||||
|
|
||||||
|
func (prc *BaseParserRuleContext) GetStart() Token {
|
||||||
|
return prc.start
|
||||||
|
}
|
||||||
|
|
||||||
|
func (prc *BaseParserRuleContext) SetStop(t Token) {
|
||||||
|
prc.stop = t
|
||||||
|
}
|
||||||
|
|
||||||
|
func (prc *BaseParserRuleContext) GetStop() Token {
|
||||||
|
return prc.stop
|
||||||
|
}
|
||||||
|
|
||||||
|
func (prc *BaseParserRuleContext) GetToken(ttype int, i int) TerminalNode {
|
||||||
|
|
||||||
|
for j := 0; j < len(prc.children); j++ {
|
||||||
|
child := prc.children[j]
|
||||||
|
if c2, ok := child.(TerminalNode); ok {
|
||||||
|
if c2.GetSymbol().GetTokenType() == ttype {
|
||||||
|
if i == 0 {
|
||||||
|
return c2
|
||||||
|
}
|
||||||
|
|
||||||
|
i--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (prc *BaseParserRuleContext) GetTokens(ttype int) []TerminalNode {
|
||||||
|
if prc.children == nil {
|
||||||
|
return make([]TerminalNode, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
tokens := make([]TerminalNode, 0)
|
||||||
|
|
||||||
|
for j := 0; j < len(prc.children); j++ {
|
||||||
|
child := prc.children[j]
|
||||||
|
if tchild, ok := child.(TerminalNode); ok {
|
||||||
|
if tchild.GetSymbol().GetTokenType() == ttype {
|
||||||
|
tokens = append(tokens, tchild)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return tokens
|
||||||
|
}
|
||||||
|
|
||||||
|
func (prc *BaseParserRuleContext) GetPayload() interface{} {
|
||||||
|
return prc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (prc *BaseParserRuleContext) getChild(ctxType reflect.Type, i int) RuleContext {
|
||||||
|
if prc.children == nil || i < 0 || i >= len(prc.children) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
j := -1 // what element have we found with ctxType?
|
||||||
|
for _, o := range prc.children {
|
||||||
|
|
||||||
|
childType := reflect.TypeOf(o)
|
||||||
|
|
||||||
|
if childType.Implements(ctxType) {
|
||||||
|
j++
|
||||||
|
if j == i {
|
||||||
|
return o.(RuleContext)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go lacks generics, so it's not possible for us to return the child with the correct type, but we do
|
||||||
|
// check for convertibility
|
||||||
|
|
||||||
|
func (prc *BaseParserRuleContext) GetTypedRuleContext(ctxType reflect.Type, i int) RuleContext {
|
||||||
|
return prc.getChild(ctxType, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (prc *BaseParserRuleContext) GetTypedRuleContexts(ctxType reflect.Type) []RuleContext {
|
||||||
|
if prc.children == nil {
|
||||||
|
return make([]RuleContext, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
contexts := make([]RuleContext, 0)
|
||||||
|
|
||||||
|
for _, child := range prc.children {
|
||||||
|
childType := reflect.TypeOf(child)
|
||||||
|
|
||||||
|
if childType.ConvertibleTo(ctxType) {
|
||||||
|
contexts = append(contexts, child.(RuleContext))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return contexts
|
||||||
|
}
|
||||||
|
|
||||||
|
func (prc *BaseParserRuleContext) GetChildCount() int {
|
||||||
|
if prc.children == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return len(prc.children)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (prc *BaseParserRuleContext) GetSourceInterval() *Interval {
|
||||||
|
if prc.start == nil || prc.stop == nil {
|
||||||
|
return TreeInvalidInterval
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewInterval(prc.start.GetTokenIndex(), prc.stop.GetTokenIndex())
|
||||||
|
}
|
||||||
|
|
||||||
|
//need to manage circular dependencies, so export now
|
||||||
|
|
||||||
|
// Print out a whole tree, not just a node, in LISP format
|
||||||
|
// (root child1 .. childN). Print just a node if b is a leaf.
|
||||||
|
//
|
||||||
|
|
||||||
|
func (prc *BaseParserRuleContext) String(ruleNames []string, stop RuleContext) string {
|
||||||
|
|
||||||
|
var p ParserRuleContext = prc
|
||||||
|
s := "["
|
||||||
|
for p != nil && p != stop {
|
||||||
|
if ruleNames == nil {
|
||||||
|
if !p.IsEmpty() {
|
||||||
|
s += strconv.Itoa(p.GetInvokingState())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ri := p.GetRuleIndex()
|
||||||
|
var ruleName string
|
||||||
|
if ri >= 0 && ri < len(ruleNames) {
|
||||||
|
ruleName = ruleNames[ri]
|
||||||
|
} else {
|
||||||
|
ruleName = strconv.Itoa(ri)
|
||||||
|
}
|
||||||
|
s += ruleName
|
||||||
|
}
|
||||||
|
if p.GetParent() != nil && (ruleNames != nil || !p.GetParent().(ParserRuleContext).IsEmpty()) {
|
||||||
|
s += " "
|
||||||
|
}
|
||||||
|
pi := p.GetParent()
|
||||||
|
if pi != nil {
|
||||||
|
p = pi.(ParserRuleContext)
|
||||||
|
} else {
|
||||||
|
p = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s += "]"
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
var ParserRuleContextEmpty = NewBaseParserRuleContext(nil, -1)
|
||||||
|
|
||||||
|
type InterpreterRuleContext interface {
|
||||||
|
ParserRuleContext
|
||||||
|
}
|
||||||
|
|
||||||
|
type BaseInterpreterRuleContext struct {
|
||||||
|
*BaseParserRuleContext
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBaseInterpreterRuleContext(parent BaseInterpreterRuleContext, invokingStateNumber, ruleIndex int) *BaseInterpreterRuleContext {
|
||||||
|
|
||||||
|
prc := new(BaseInterpreterRuleContext)
|
||||||
|
|
||||||
|
prc.BaseParserRuleContext = NewBaseParserRuleContext(parent, invokingStateNumber)
|
||||||
|
|
||||||
|
prc.RuleIndex = ruleIndex
|
||||||
|
|
||||||
|
return prc
|
||||||
|
}
|
||||||
806
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/prediction_context.go
generated
vendored
Normal file
806
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/prediction_context.go
generated
vendored
Normal file
@@ -0,0 +1,806 @@
|
|||||||
|
// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
||||||
|
// Use of this file is governed by the BSD 3-clause license that
|
||||||
|
// can be found in the LICENSE.txt file in the project root.
|
||||||
|
|
||||||
|
package antlr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"golang.org/x/exp/slices"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Represents {@code $} in local context prediction, which means wildcard.
|
||||||
|
// {@code//+x =//}.
|
||||||
|
// /
|
||||||
|
const (
|
||||||
|
BasePredictionContextEmptyReturnState = 0x7FFFFFFF
|
||||||
|
)
|
||||||
|
|
||||||
|
// Represents {@code $} in an array in full context mode, when {@code $}
|
||||||
|
// doesn't mean wildcard: {@code $ + x = [$,x]}. Here,
|
||||||
|
// {@code $} = {@link //EmptyReturnState}.
|
||||||
|
// /
|
||||||
|
|
||||||
|
var (
|
||||||
|
BasePredictionContextglobalNodeCount = 1
|
||||||
|
BasePredictionContextid = BasePredictionContextglobalNodeCount
|
||||||
|
)
|
||||||
|
|
||||||
|
type PredictionContext interface {
|
||||||
|
Hash() int
|
||||||
|
Equals(interface{}) bool
|
||||||
|
GetParent(int) PredictionContext
|
||||||
|
getReturnState(int) int
|
||||||
|
length() int
|
||||||
|
isEmpty() bool
|
||||||
|
hasEmptyPath() bool
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type BasePredictionContext struct {
|
||||||
|
cachedHash int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBasePredictionContext(cachedHash int) *BasePredictionContext {
|
||||||
|
pc := new(BasePredictionContext)
|
||||||
|
pc.cachedHash = cachedHash
|
||||||
|
|
||||||
|
return pc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BasePredictionContext) isEmpty() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func calculateHash(parent PredictionContext, returnState int) int {
|
||||||
|
h := murmurInit(1)
|
||||||
|
h = murmurUpdate(h, parent.Hash())
|
||||||
|
h = murmurUpdate(h, returnState)
|
||||||
|
return murmurFinish(h, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _emptyPredictionContextHash int
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
_emptyPredictionContextHash = murmurInit(1)
|
||||||
|
_emptyPredictionContextHash = murmurFinish(_emptyPredictionContextHash, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func calculateEmptyHash() int {
|
||||||
|
return _emptyPredictionContextHash
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used to cache {@link BasePredictionContext} objects. Its used for the shared
|
||||||
|
// context cash associated with contexts in DFA states. This cache
|
||||||
|
// can be used for both lexers and parsers.
|
||||||
|
|
||||||
|
type PredictionContextCache struct {
|
||||||
|
cache map[PredictionContext]PredictionContext
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPredictionContextCache() *PredictionContextCache {
|
||||||
|
t := new(PredictionContextCache)
|
||||||
|
t.cache = make(map[PredictionContext]PredictionContext)
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a context to the cache and return it. If the context already exists,
|
||||||
|
// return that one instead and do not add a Newcontext to the cache.
|
||||||
|
// Protect shared cache from unsafe thread access.
|
||||||
|
func (p *PredictionContextCache) add(ctx PredictionContext) PredictionContext {
|
||||||
|
if ctx == BasePredictionContextEMPTY {
|
||||||
|
return BasePredictionContextEMPTY
|
||||||
|
}
|
||||||
|
existing := p.cache[ctx]
|
||||||
|
if existing != nil {
|
||||||
|
return existing
|
||||||
|
}
|
||||||
|
p.cache[ctx] = ctx
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PredictionContextCache) Get(ctx PredictionContext) PredictionContext {
|
||||||
|
return p.cache[ctx]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PredictionContextCache) length() int {
|
||||||
|
return len(p.cache)
|
||||||
|
}
|
||||||
|
|
||||||
|
type SingletonPredictionContext interface {
|
||||||
|
PredictionContext
|
||||||
|
}
|
||||||
|
|
||||||
|
type BaseSingletonPredictionContext struct {
|
||||||
|
*BasePredictionContext
|
||||||
|
|
||||||
|
parentCtx PredictionContext
|
||||||
|
returnState int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBaseSingletonPredictionContext(parent PredictionContext, returnState int) *BaseSingletonPredictionContext {
|
||||||
|
var cachedHash int
|
||||||
|
if parent != nil {
|
||||||
|
cachedHash = calculateHash(parent, returnState)
|
||||||
|
} else {
|
||||||
|
cachedHash = calculateEmptyHash()
|
||||||
|
}
|
||||||
|
|
||||||
|
s := new(BaseSingletonPredictionContext)
|
||||||
|
s.BasePredictionContext = NewBasePredictionContext(cachedHash)
|
||||||
|
|
||||||
|
s.parentCtx = parent
|
||||||
|
s.returnState = returnState
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func SingletonBasePredictionContextCreate(parent PredictionContext, returnState int) PredictionContext {
|
||||||
|
if returnState == BasePredictionContextEmptyReturnState && parent == nil {
|
||||||
|
// someone can pass in the bits of an array ctx that mean $
|
||||||
|
return BasePredictionContextEMPTY
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewBaseSingletonPredictionContext(parent, returnState)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseSingletonPredictionContext) length() int {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseSingletonPredictionContext) GetParent(index int) PredictionContext {
|
||||||
|
return b.parentCtx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseSingletonPredictionContext) getReturnState(index int) int {
|
||||||
|
return b.returnState
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseSingletonPredictionContext) hasEmptyPath() bool {
|
||||||
|
return b.returnState == BasePredictionContextEmptyReturnState
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseSingletonPredictionContext) Hash() int {
|
||||||
|
return b.cachedHash
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseSingletonPredictionContext) Equals(other interface{}) bool {
|
||||||
|
if b == other {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if _, ok := other.(*BaseSingletonPredictionContext); !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
otherP := other.(*BaseSingletonPredictionContext)
|
||||||
|
|
||||||
|
if b.returnState != otherP.getReturnState(0) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if b.parentCtx == nil {
|
||||||
|
return otherP.parentCtx == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.parentCtx.Equals(otherP.parentCtx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseSingletonPredictionContext) String() string {
|
||||||
|
var up string
|
||||||
|
|
||||||
|
if b.parentCtx == nil {
|
||||||
|
up = ""
|
||||||
|
} else {
|
||||||
|
up = b.parentCtx.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(up) == 0 {
|
||||||
|
if b.returnState == BasePredictionContextEmptyReturnState {
|
||||||
|
return "$"
|
||||||
|
}
|
||||||
|
|
||||||
|
return strconv.Itoa(b.returnState)
|
||||||
|
}
|
||||||
|
|
||||||
|
return strconv.Itoa(b.returnState) + " " + up
|
||||||
|
}
|
||||||
|
|
||||||
|
var BasePredictionContextEMPTY = NewEmptyPredictionContext()
|
||||||
|
|
||||||
|
type EmptyPredictionContext struct {
|
||||||
|
*BaseSingletonPredictionContext
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewEmptyPredictionContext() *EmptyPredictionContext {
|
||||||
|
|
||||||
|
p := new(EmptyPredictionContext)
|
||||||
|
|
||||||
|
p.BaseSingletonPredictionContext = NewBaseSingletonPredictionContext(nil, BasePredictionContextEmptyReturnState)
|
||||||
|
p.cachedHash = calculateEmptyHash()
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EmptyPredictionContext) isEmpty() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EmptyPredictionContext) GetParent(index int) PredictionContext {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EmptyPredictionContext) getReturnState(index int) int {
|
||||||
|
return e.returnState
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EmptyPredictionContext) Hash() int {
|
||||||
|
return e.cachedHash
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EmptyPredictionContext) Equals(other interface{}) bool {
|
||||||
|
return e == other
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EmptyPredictionContext) String() string {
|
||||||
|
return "$"
|
||||||
|
}
|
||||||
|
|
||||||
|
type ArrayPredictionContext struct {
|
||||||
|
*BasePredictionContext
|
||||||
|
|
||||||
|
parents []PredictionContext
|
||||||
|
returnStates []int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewArrayPredictionContext(parents []PredictionContext, returnStates []int) *ArrayPredictionContext {
|
||||||
|
// Parent can be nil only if full ctx mode and we make an array
|
||||||
|
// from {@link //EMPTY} and non-empty. We merge {@link //EMPTY} by using
|
||||||
|
// nil parent and
|
||||||
|
// returnState == {@link //EmptyReturnState}.
|
||||||
|
hash := murmurInit(1)
|
||||||
|
|
||||||
|
for _, parent := range parents {
|
||||||
|
hash = murmurUpdate(hash, parent.Hash())
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, returnState := range returnStates {
|
||||||
|
hash = murmurUpdate(hash, returnState)
|
||||||
|
}
|
||||||
|
|
||||||
|
hash = murmurFinish(hash, len(parents)<<1)
|
||||||
|
|
||||||
|
c := new(ArrayPredictionContext)
|
||||||
|
c.BasePredictionContext = NewBasePredictionContext(hash)
|
||||||
|
|
||||||
|
c.parents = parents
|
||||||
|
c.returnStates = returnStates
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ArrayPredictionContext) GetReturnStates() []int {
|
||||||
|
return a.returnStates
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ArrayPredictionContext) hasEmptyPath() bool {
|
||||||
|
return a.getReturnState(a.length()-1) == BasePredictionContextEmptyReturnState
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ArrayPredictionContext) isEmpty() bool {
|
||||||
|
// since EmptyReturnState can only appear in the last position, we
|
||||||
|
// don't need to verify that size==1
|
||||||
|
return a.returnStates[0] == BasePredictionContextEmptyReturnState
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ArrayPredictionContext) length() int {
|
||||||
|
return len(a.returnStates)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ArrayPredictionContext) GetParent(index int) PredictionContext {
|
||||||
|
return a.parents[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ArrayPredictionContext) getReturnState(index int) int {
|
||||||
|
return a.returnStates[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equals is the default comparison function for ArrayPredictionContext when no specialized
|
||||||
|
// implementation is needed for a collection
|
||||||
|
func (a *ArrayPredictionContext) Equals(o interface{}) bool {
|
||||||
|
if a == o {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
other, ok := o.(*ArrayPredictionContext)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if a.cachedHash != other.Hash() {
|
||||||
|
return false // can't be same if hash is different
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must compare the actual array elements and not just the array address
|
||||||
|
//
|
||||||
|
return slices.Equal(a.returnStates, other.returnStates) &&
|
||||||
|
slices.EqualFunc(a.parents, other.parents, func(x, y PredictionContext) bool {
|
||||||
|
return x.Equals(y)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash is the default hash function for ArrayPredictionContext when no specialized
|
||||||
|
// implementation is needed for a collection
|
||||||
|
func (a *ArrayPredictionContext) Hash() int {
|
||||||
|
return a.BasePredictionContext.cachedHash
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ArrayPredictionContext) String() string {
|
||||||
|
if a.isEmpty() {
|
||||||
|
return "[]"
|
||||||
|
}
|
||||||
|
|
||||||
|
s := "["
|
||||||
|
for i := 0; i < len(a.returnStates); i++ {
|
||||||
|
if i > 0 {
|
||||||
|
s = s + ", "
|
||||||
|
}
|
||||||
|
if a.returnStates[i] == BasePredictionContextEmptyReturnState {
|
||||||
|
s = s + "$"
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
s = s + strconv.Itoa(a.returnStates[i])
|
||||||
|
if a.parents[i] != nil {
|
||||||
|
s = s + " " + a.parents[i].String()
|
||||||
|
} else {
|
||||||
|
s = s + "nil"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return s + "]"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert a {@link RuleContext} tree to a {@link BasePredictionContext} graph.
|
||||||
|
// Return {@link //EMPTY} if {@code outerContext} is empty or nil.
|
||||||
|
// /
|
||||||
|
func predictionContextFromRuleContext(a *ATN, outerContext RuleContext) PredictionContext {
|
||||||
|
if outerContext == nil {
|
||||||
|
outerContext = ParserRuleContextEmpty
|
||||||
|
}
|
||||||
|
// if we are in RuleContext of start rule, s, then BasePredictionContext
|
||||||
|
// is EMPTY. Nobody called us. (if we are empty, return empty)
|
||||||
|
if outerContext.GetParent() == nil || outerContext == ParserRuleContextEmpty {
|
||||||
|
return BasePredictionContextEMPTY
|
||||||
|
}
|
||||||
|
// If we have a parent, convert it to a BasePredictionContext graph
|
||||||
|
parent := predictionContextFromRuleContext(a, outerContext.GetParent().(RuleContext))
|
||||||
|
state := a.states[outerContext.GetInvokingState()]
|
||||||
|
transition := state.GetTransitions()[0]
|
||||||
|
|
||||||
|
return SingletonBasePredictionContextCreate(parent, transition.(*RuleTransition).followState.GetStateNumber())
|
||||||
|
}
|
||||||
|
|
||||||
|
func merge(a, b PredictionContext, rootIsWildcard bool, mergeCache *DoubleDict) PredictionContext {
|
||||||
|
|
||||||
|
// Share same graph if both same
|
||||||
|
//
|
||||||
|
if a == b || a.Equals(b) {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
// In Java, EmptyPredictionContext inherits from SingletonPredictionContext, and so the test
|
||||||
|
// in java for SingletonPredictionContext will succeed and a new ArrayPredictionContext will be created
|
||||||
|
// from it.
|
||||||
|
// In go, EmptyPredictionContext does not equate to SingletonPredictionContext and so that conversion
|
||||||
|
// will fail. We need to test for both Empty and Singleton and create an ArrayPredictionContext from
|
||||||
|
// either of them.
|
||||||
|
|
||||||
|
ac, ok1 := a.(*BaseSingletonPredictionContext)
|
||||||
|
bc, ok2 := b.(*BaseSingletonPredictionContext)
|
||||||
|
|
||||||
|
if ok1 && ok2 {
|
||||||
|
return mergeSingletons(ac, bc, rootIsWildcard, mergeCache)
|
||||||
|
}
|
||||||
|
// At least one of a or b is array
|
||||||
|
// If one is $ and rootIsWildcard, return $ as// wildcard
|
||||||
|
if rootIsWildcard {
|
||||||
|
if _, ok := a.(*EmptyPredictionContext); ok {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
if _, ok := b.(*EmptyPredictionContext); ok {
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert Singleton or Empty so both are arrays to normalize - We should not use the existing parameters
|
||||||
|
// here.
|
||||||
|
//
|
||||||
|
// TODO: I think that maybe the Prediction Context structs should be redone as there is a chance we will see this mess again - maybe redo the logic here
|
||||||
|
|
||||||
|
var arp, arb *ArrayPredictionContext
|
||||||
|
var ok bool
|
||||||
|
if arp, ok = a.(*ArrayPredictionContext); ok {
|
||||||
|
} else if _, ok = a.(*BaseSingletonPredictionContext); ok {
|
||||||
|
arp = NewArrayPredictionContext([]PredictionContext{a.GetParent(0)}, []int{a.getReturnState(0)})
|
||||||
|
} else if _, ok = a.(*EmptyPredictionContext); ok {
|
||||||
|
arp = NewArrayPredictionContext([]PredictionContext{}, []int{})
|
||||||
|
}
|
||||||
|
|
||||||
|
if arb, ok = b.(*ArrayPredictionContext); ok {
|
||||||
|
} else if _, ok = b.(*BaseSingletonPredictionContext); ok {
|
||||||
|
arb = NewArrayPredictionContext([]PredictionContext{b.GetParent(0)}, []int{b.getReturnState(0)})
|
||||||
|
} else if _, ok = b.(*EmptyPredictionContext); ok {
|
||||||
|
arb = NewArrayPredictionContext([]PredictionContext{}, []int{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Both arp and arb
|
||||||
|
return mergeArrays(arp, arb, rootIsWildcard, mergeCache)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge two {@link SingletonBasePredictionContext} instances.
|
||||||
|
//
|
||||||
|
// <p>Stack tops equal, parents merge is same return left graph.<br>
|
||||||
|
// <embed src="images/SingletonMerge_SameRootSamePar.svg"
|
||||||
|
// type="image/svg+xml"/></p>
|
||||||
|
//
|
||||||
|
// <p>Same stack top, parents differ merge parents giving array node, then
|
||||||
|
// remainders of those graphs. A Newroot node is created to point to the
|
||||||
|
// merged parents.<br>
|
||||||
|
// <embed src="images/SingletonMerge_SameRootDiffPar.svg"
|
||||||
|
// type="image/svg+xml"/></p>
|
||||||
|
//
|
||||||
|
// <p>Different stack tops pointing to same parent. Make array node for the
|
||||||
|
// root where both element in the root point to the same (original)
|
||||||
|
// parent.<br>
|
||||||
|
// <embed src="images/SingletonMerge_DiffRootSamePar.svg"
|
||||||
|
// type="image/svg+xml"/></p>
|
||||||
|
//
|
||||||
|
// <p>Different stack tops pointing to different parents. Make array node for
|
||||||
|
// the root where each element points to the corresponding original
|
||||||
|
// parent.<br>
|
||||||
|
// <embed src="images/SingletonMerge_DiffRootDiffPar.svg"
|
||||||
|
// type="image/svg+xml"/></p>
|
||||||
|
//
|
||||||
|
// @param a the first {@link SingletonBasePredictionContext}
|
||||||
|
// @param b the second {@link SingletonBasePredictionContext}
|
||||||
|
// @param rootIsWildcard {@code true} if this is a local-context merge,
|
||||||
|
// otherwise false to indicate a full-context merge
|
||||||
|
// @param mergeCache
|
||||||
|
// /
|
||||||
|
func mergeSingletons(a, b *BaseSingletonPredictionContext, rootIsWildcard bool, mergeCache *DoubleDict) PredictionContext {
|
||||||
|
if mergeCache != nil {
|
||||||
|
previous := mergeCache.Get(a.Hash(), b.Hash())
|
||||||
|
if previous != nil {
|
||||||
|
return previous.(PredictionContext)
|
||||||
|
}
|
||||||
|
previous = mergeCache.Get(b.Hash(), a.Hash())
|
||||||
|
if previous != nil {
|
||||||
|
return previous.(PredictionContext)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rootMerge := mergeRoot(a, b, rootIsWildcard)
|
||||||
|
if rootMerge != nil {
|
||||||
|
if mergeCache != nil {
|
||||||
|
mergeCache.set(a.Hash(), b.Hash(), rootMerge)
|
||||||
|
}
|
||||||
|
return rootMerge
|
||||||
|
}
|
||||||
|
if a.returnState == b.returnState {
|
||||||
|
parent := merge(a.parentCtx, b.parentCtx, rootIsWildcard, mergeCache)
|
||||||
|
// if parent is same as existing a or b parent or reduced to a parent,
|
||||||
|
// return it
|
||||||
|
if parent == a.parentCtx {
|
||||||
|
return a // ax + bx = ax, if a=b
|
||||||
|
}
|
||||||
|
if parent == b.parentCtx {
|
||||||
|
return b // ax + bx = bx, if a=b
|
||||||
|
}
|
||||||
|
// else: ax + ay = a'[x,y]
|
||||||
|
// merge parents x and y, giving array node with x,y then remainders
|
||||||
|
// of those graphs. dup a, a' points at merged array
|
||||||
|
// Newjoined parent so create Newsingleton pointing to it, a'
|
||||||
|
spc := SingletonBasePredictionContextCreate(parent, a.returnState)
|
||||||
|
if mergeCache != nil {
|
||||||
|
mergeCache.set(a.Hash(), b.Hash(), spc)
|
||||||
|
}
|
||||||
|
return spc
|
||||||
|
}
|
||||||
|
// a != b payloads differ
|
||||||
|
// see if we can collapse parents due to $+x parents if local ctx
|
||||||
|
var singleParent PredictionContext
|
||||||
|
if a == b || (a.parentCtx != nil && a.parentCtx == b.parentCtx) { // ax +
|
||||||
|
// bx =
|
||||||
|
// [a,b]x
|
||||||
|
singleParent = a.parentCtx
|
||||||
|
}
|
||||||
|
if singleParent != nil { // parents are same
|
||||||
|
// sort payloads and use same parent
|
||||||
|
payloads := []int{a.returnState, b.returnState}
|
||||||
|
if a.returnState > b.returnState {
|
||||||
|
payloads[0] = b.returnState
|
||||||
|
payloads[1] = a.returnState
|
||||||
|
}
|
||||||
|
parents := []PredictionContext{singleParent, singleParent}
|
||||||
|
apc := NewArrayPredictionContext(parents, payloads)
|
||||||
|
if mergeCache != nil {
|
||||||
|
mergeCache.set(a.Hash(), b.Hash(), apc)
|
||||||
|
}
|
||||||
|
return apc
|
||||||
|
}
|
||||||
|
// parents differ and can't merge them. Just pack together
|
||||||
|
// into array can't merge.
|
||||||
|
// ax + by = [ax,by]
|
||||||
|
payloads := []int{a.returnState, b.returnState}
|
||||||
|
parents := []PredictionContext{a.parentCtx, b.parentCtx}
|
||||||
|
if a.returnState > b.returnState { // sort by payload
|
||||||
|
payloads[0] = b.returnState
|
||||||
|
payloads[1] = a.returnState
|
||||||
|
parents = []PredictionContext{b.parentCtx, a.parentCtx}
|
||||||
|
}
|
||||||
|
apc := NewArrayPredictionContext(parents, payloads)
|
||||||
|
if mergeCache != nil {
|
||||||
|
mergeCache.set(a.Hash(), b.Hash(), apc)
|
||||||
|
}
|
||||||
|
return apc
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle case where at least one of {@code a} or {@code b} is
|
||||||
|
// {@link //EMPTY}. In the following diagrams, the symbol {@code $} is used
|
||||||
|
// to represent {@link //EMPTY}.
|
||||||
|
//
|
||||||
|
// <h2>Local-Context Merges</h2>
|
||||||
|
//
|
||||||
|
// <p>These local-context merge operations are used when {@code rootIsWildcard}
|
||||||
|
// is true.</p>
|
||||||
|
//
|
||||||
|
// <p>{@link //EMPTY} is superset of any graph return {@link //EMPTY}.<br>
|
||||||
|
// <embed src="images/LocalMerge_EmptyRoot.svg" type="image/svg+xml"/></p>
|
||||||
|
//
|
||||||
|
// <p>{@link //EMPTY} and anything is {@code //EMPTY}, so merged parent is
|
||||||
|
// {@code //EMPTY} return left graph.<br>
|
||||||
|
// <embed src="images/LocalMerge_EmptyParent.svg" type="image/svg+xml"/></p>
|
||||||
|
//
|
||||||
|
// <p>Special case of last merge if local context.<br>
|
||||||
|
// <embed src="images/LocalMerge_DiffRoots.svg" type="image/svg+xml"/></p>
|
||||||
|
//
|
||||||
|
// <h2>Full-Context Merges</h2>
|
||||||
|
//
|
||||||
|
// <p>These full-context merge operations are used when {@code rootIsWildcard}
|
||||||
|
// is false.</p>
|
||||||
|
//
|
||||||
|
// <p><embed src="images/FullMerge_EmptyRoots.svg" type="image/svg+xml"/></p>
|
||||||
|
//
|
||||||
|
// <p>Must keep all contexts {@link //EMPTY} in array is a special value (and
|
||||||
|
// nil parent).<br>
|
||||||
|
// <embed src="images/FullMerge_EmptyRoot.svg" type="image/svg+xml"/></p>
|
||||||
|
//
|
||||||
|
// <p><embed src="images/FullMerge_SameRoot.svg" type="image/svg+xml"/></p>
|
||||||
|
//
|
||||||
|
// @param a the first {@link SingletonBasePredictionContext}
|
||||||
|
// @param b the second {@link SingletonBasePredictionContext}
|
||||||
|
// @param rootIsWildcard {@code true} if this is a local-context merge,
|
||||||
|
// otherwise false to indicate a full-context merge
|
||||||
|
// /
|
||||||
|
func mergeRoot(a, b SingletonPredictionContext, rootIsWildcard bool) PredictionContext {
|
||||||
|
if rootIsWildcard {
|
||||||
|
if a == BasePredictionContextEMPTY {
|
||||||
|
return BasePredictionContextEMPTY // // + b =//
|
||||||
|
}
|
||||||
|
if b == BasePredictionContextEMPTY {
|
||||||
|
return BasePredictionContextEMPTY // a +// =//
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if a == BasePredictionContextEMPTY && b == BasePredictionContextEMPTY {
|
||||||
|
return BasePredictionContextEMPTY // $ + $ = $
|
||||||
|
} else if a == BasePredictionContextEMPTY { // $ + x = [$,x]
|
||||||
|
payloads := []int{b.getReturnState(-1), BasePredictionContextEmptyReturnState}
|
||||||
|
parents := []PredictionContext{b.GetParent(-1), nil}
|
||||||
|
return NewArrayPredictionContext(parents, payloads)
|
||||||
|
} else if b == BasePredictionContextEMPTY { // x + $ = [$,x] ($ is always first if present)
|
||||||
|
payloads := []int{a.getReturnState(-1), BasePredictionContextEmptyReturnState}
|
||||||
|
parents := []PredictionContext{a.GetParent(-1), nil}
|
||||||
|
return NewArrayPredictionContext(parents, payloads)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge two {@link ArrayBasePredictionContext} instances.
|
||||||
|
//
|
||||||
|
// <p>Different tops, different parents.<br>
|
||||||
|
// <embed src="images/ArrayMerge_DiffTopDiffPar.svg" type="image/svg+xml"/></p>
|
||||||
|
//
|
||||||
|
// <p>Shared top, same parents.<br>
|
||||||
|
// <embed src="images/ArrayMerge_ShareTopSamePar.svg" type="image/svg+xml"/></p>
|
||||||
|
//
|
||||||
|
// <p>Shared top, different parents.<br>
|
||||||
|
// <embed src="images/ArrayMerge_ShareTopDiffPar.svg" type="image/svg+xml"/></p>
|
||||||
|
//
|
||||||
|
// <p>Shared top, all shared parents.<br>
|
||||||
|
// <embed src="images/ArrayMerge_ShareTopSharePar.svg"
|
||||||
|
// type="image/svg+xml"/></p>
|
||||||
|
//
|
||||||
|
// <p>Equal tops, merge parents and reduce top to
|
||||||
|
// {@link SingletonBasePredictionContext}.<br>
|
||||||
|
// <embed src="images/ArrayMerge_EqualTop.svg" type="image/svg+xml"/></p>
|
||||||
|
// /
|
||||||
|
func mergeArrays(a, b *ArrayPredictionContext, rootIsWildcard bool, mergeCache *DoubleDict) PredictionContext {
|
||||||
|
if mergeCache != nil {
|
||||||
|
previous := mergeCache.Get(a.Hash(), b.Hash())
|
||||||
|
if previous != nil {
|
||||||
|
if ParserATNSimulatorTraceATNSim {
|
||||||
|
fmt.Println("mergeArrays a=" + a.String() + ",b=" + b.String() + " -> previous")
|
||||||
|
}
|
||||||
|
return previous.(PredictionContext)
|
||||||
|
}
|
||||||
|
previous = mergeCache.Get(b.Hash(), a.Hash())
|
||||||
|
if previous != nil {
|
||||||
|
if ParserATNSimulatorTraceATNSim {
|
||||||
|
fmt.Println("mergeArrays a=" + a.String() + ",b=" + b.String() + " -> previous")
|
||||||
|
}
|
||||||
|
return previous.(PredictionContext)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// merge sorted payloads a + b => M
|
||||||
|
i := 0 // walks a
|
||||||
|
j := 0 // walks b
|
||||||
|
k := 0 // walks target M array
|
||||||
|
|
||||||
|
mergedReturnStates := make([]int, len(a.returnStates)+len(b.returnStates))
|
||||||
|
mergedParents := make([]PredictionContext, len(a.returnStates)+len(b.returnStates))
|
||||||
|
// walk and merge to yield mergedParents, mergedReturnStates
|
||||||
|
for i < len(a.returnStates) && j < len(b.returnStates) {
|
||||||
|
aParent := a.parents[i]
|
||||||
|
bParent := b.parents[j]
|
||||||
|
if a.returnStates[i] == b.returnStates[j] {
|
||||||
|
// same payload (stack tops are equal), must yield merged singleton
|
||||||
|
payload := a.returnStates[i]
|
||||||
|
// $+$ = $
|
||||||
|
bothDollars := payload == BasePredictionContextEmptyReturnState && aParent == nil && bParent == nil
|
||||||
|
axAX := aParent != nil && bParent != nil && aParent == bParent // ax+ax
|
||||||
|
// ->
|
||||||
|
// ax
|
||||||
|
if bothDollars || axAX {
|
||||||
|
mergedParents[k] = aParent // choose left
|
||||||
|
mergedReturnStates[k] = payload
|
||||||
|
} else { // ax+ay -> a'[x,y]
|
||||||
|
mergedParent := merge(aParent, bParent, rootIsWildcard, mergeCache)
|
||||||
|
mergedParents[k] = mergedParent
|
||||||
|
mergedReturnStates[k] = payload
|
||||||
|
}
|
||||||
|
i++ // hop over left one as usual
|
||||||
|
j++ // but also Skip one in right side since we merge
|
||||||
|
} else if a.returnStates[i] < b.returnStates[j] { // copy a[i] to M
|
||||||
|
mergedParents[k] = aParent
|
||||||
|
mergedReturnStates[k] = a.returnStates[i]
|
||||||
|
i++
|
||||||
|
} else { // b > a, copy b[j] to M
|
||||||
|
mergedParents[k] = bParent
|
||||||
|
mergedReturnStates[k] = b.returnStates[j]
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
k++
|
||||||
|
}
|
||||||
|
// copy over any payloads remaining in either array
|
||||||
|
if i < len(a.returnStates) {
|
||||||
|
for p := i; p < len(a.returnStates); p++ {
|
||||||
|
mergedParents[k] = a.parents[p]
|
||||||
|
mergedReturnStates[k] = a.returnStates[p]
|
||||||
|
k++
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for p := j; p < len(b.returnStates); p++ {
|
||||||
|
mergedParents[k] = b.parents[p]
|
||||||
|
mergedReturnStates[k] = b.returnStates[p]
|
||||||
|
k++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// trim merged if we combined a few that had same stack tops
|
||||||
|
if k < len(mergedParents) { // write index < last position trim
|
||||||
|
if k == 1 { // for just one merged element, return singleton top
|
||||||
|
pc := SingletonBasePredictionContextCreate(mergedParents[0], mergedReturnStates[0])
|
||||||
|
if mergeCache != nil {
|
||||||
|
mergeCache.set(a.Hash(), b.Hash(), pc)
|
||||||
|
}
|
||||||
|
return pc
|
||||||
|
}
|
||||||
|
mergedParents = mergedParents[0:k]
|
||||||
|
mergedReturnStates = mergedReturnStates[0:k]
|
||||||
|
}
|
||||||
|
|
||||||
|
M := NewArrayPredictionContext(mergedParents, mergedReturnStates)
|
||||||
|
|
||||||
|
// if we created same array as a or b, return that instead
|
||||||
|
// TODO: track whether this is possible above during merge sort for speed
|
||||||
|
// TODO: In go, I do not think we can just do M == xx as M is a brand new allocation. This could be causing allocation problems
|
||||||
|
if M == a {
|
||||||
|
if mergeCache != nil {
|
||||||
|
mergeCache.set(a.Hash(), b.Hash(), a)
|
||||||
|
}
|
||||||
|
if ParserATNSimulatorTraceATNSim {
|
||||||
|
fmt.Println("mergeArrays a=" + a.String() + ",b=" + b.String() + " -> a")
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
if M == b {
|
||||||
|
if mergeCache != nil {
|
||||||
|
mergeCache.set(a.Hash(), b.Hash(), b)
|
||||||
|
}
|
||||||
|
if ParserATNSimulatorTraceATNSim {
|
||||||
|
fmt.Println("mergeArrays a=" + a.String() + ",b=" + b.String() + " -> b")
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
combineCommonParents(mergedParents)
|
||||||
|
|
||||||
|
if mergeCache != nil {
|
||||||
|
mergeCache.set(a.Hash(), b.Hash(), M)
|
||||||
|
}
|
||||||
|
if ParserATNSimulatorTraceATNSim {
|
||||||
|
fmt.Println("mergeArrays a=" + a.String() + ",b=" + b.String() + " -> " + M.String())
|
||||||
|
}
|
||||||
|
return M
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make pass over all <em>M</em> {@code parents} merge any {@code equals()}
|
||||||
|
// ones.
|
||||||
|
// /
|
||||||
|
func combineCommonParents(parents []PredictionContext) {
|
||||||
|
uniqueParents := make(map[PredictionContext]PredictionContext)
|
||||||
|
|
||||||
|
for p := 0; p < len(parents); p++ {
|
||||||
|
parent := parents[p]
|
||||||
|
if uniqueParents[parent] == nil {
|
||||||
|
uniqueParents[parent] = parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for q := 0; q < len(parents); q++ {
|
||||||
|
parents[q] = uniqueParents[parents[q]]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCachedBasePredictionContext(context PredictionContext, contextCache *PredictionContextCache, visited map[PredictionContext]PredictionContext) PredictionContext {
|
||||||
|
|
||||||
|
if context.isEmpty() {
|
||||||
|
return context
|
||||||
|
}
|
||||||
|
existing := visited[context]
|
||||||
|
if existing != nil {
|
||||||
|
return existing
|
||||||
|
}
|
||||||
|
existing = contextCache.Get(context)
|
||||||
|
if existing != nil {
|
||||||
|
visited[context] = existing
|
||||||
|
return existing
|
||||||
|
}
|
||||||
|
changed := false
|
||||||
|
parents := make([]PredictionContext, context.length())
|
||||||
|
for i := 0; i < len(parents); i++ {
|
||||||
|
parent := getCachedBasePredictionContext(context.GetParent(i), contextCache, visited)
|
||||||
|
if changed || parent != context.GetParent(i) {
|
||||||
|
if !changed {
|
||||||
|
parents = make([]PredictionContext, context.length())
|
||||||
|
for j := 0; j < context.length(); j++ {
|
||||||
|
parents[j] = context.GetParent(j)
|
||||||
|
}
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
parents[i] = parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !changed {
|
||||||
|
contextCache.add(context)
|
||||||
|
visited[context] = context
|
||||||
|
return context
|
||||||
|
}
|
||||||
|
var updated PredictionContext
|
||||||
|
if len(parents) == 0 {
|
||||||
|
updated = BasePredictionContextEMPTY
|
||||||
|
} else if len(parents) == 1 {
|
||||||
|
updated = SingletonBasePredictionContextCreate(parents[0], context.getReturnState(0))
|
||||||
|
} else {
|
||||||
|
updated = NewArrayPredictionContext(parents, context.(*ArrayPredictionContext).GetReturnStates())
|
||||||
|
}
|
||||||
|
contextCache.add(updated)
|
||||||
|
visited[updated] = updated
|
||||||
|
visited[context] = updated
|
||||||
|
|
||||||
|
return updated
|
||||||
|
}
|
||||||
529
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/prediction_mode.go
generated
vendored
Normal file
529
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/prediction_mode.go
generated
vendored
Normal file
@@ -0,0 +1,529 @@
|
|||||||
|
// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
||||||
|
// Use of this file is governed by the BSD 3-clause license that
|
||||||
|
// can be found in the LICENSE.txt file in the project root.
|
||||||
|
|
||||||
|
package antlr
|
||||||
|
|
||||||
|
// This enumeration defines the prediction modes available in ANTLR 4 along with
|
||||||
|
// utility methods for analyzing configuration sets for conflicts and/or
|
||||||
|
// ambiguities.
|
||||||
|
|
||||||
|
const (
|
||||||
|
//
|
||||||
|
// The SLL(*) prediction mode. This prediction mode ignores the current
|
||||||
|
// parser context when making predictions. This is the fastest prediction
|
||||||
|
// mode, and provides correct results for many grammars. This prediction
|
||||||
|
// mode is more powerful than the prediction mode provided by ANTLR 3, but
|
||||||
|
// may result in syntax errors for grammar and input combinations which are
|
||||||
|
// not SLL.
|
||||||
|
//
|
||||||
|
// <p>
|
||||||
|
// When using this prediction mode, the parser will either return a correct
|
||||||
|
// parse tree (i.e. the same parse tree that would be returned with the
|
||||||
|
// {@link //LL} prediction mode), or it will Report a syntax error. If a
|
||||||
|
// syntax error is encountered when using the {@link //SLL} prediction mode,
|
||||||
|
// it may be due to either an actual syntax error in the input or indicate
|
||||||
|
// that the particular combination of grammar and input requires the more
|
||||||
|
// powerful {@link //LL} prediction abilities to complete successfully.</p>
|
||||||
|
//
|
||||||
|
// <p>
|
||||||
|
// This prediction mode does not provide any guarantees for prediction
|
||||||
|
// behavior for syntactically-incorrect inputs.</p>
|
||||||
|
//
|
||||||
|
PredictionModeSLL = 0
|
||||||
|
//
|
||||||
|
// The LL(*) prediction mode. This prediction mode allows the current parser
|
||||||
|
// context to be used for resolving SLL conflicts that occur during
|
||||||
|
// prediction. This is the fastest prediction mode that guarantees correct
|
||||||
|
// parse results for all combinations of grammars with syntactically correct
|
||||||
|
// inputs.
|
||||||
|
//
|
||||||
|
// <p>
|
||||||
|
// When using this prediction mode, the parser will make correct decisions
|
||||||
|
// for all syntactically-correct grammar and input combinations. However, in
|
||||||
|
// cases where the grammar is truly ambiguous this prediction mode might not
|
||||||
|
// Report a precise answer for <em>exactly which</em> alternatives are
|
||||||
|
// ambiguous.</p>
|
||||||
|
//
|
||||||
|
// <p>
|
||||||
|
// This prediction mode does not provide any guarantees for prediction
|
||||||
|
// behavior for syntactically-incorrect inputs.</p>
|
||||||
|
//
|
||||||
|
PredictionModeLL = 1
|
||||||
|
//
|
||||||
|
// The LL(*) prediction mode with exact ambiguity detection. In addition to
|
||||||
|
// the correctness guarantees provided by the {@link //LL} prediction mode,
|
||||||
|
// this prediction mode instructs the prediction algorithm to determine the
|
||||||
|
// complete and exact set of ambiguous alternatives for every ambiguous
|
||||||
|
// decision encountered while parsing.
|
||||||
|
//
|
||||||
|
// <p>
|
||||||
|
// This prediction mode may be used for diagnosing ambiguities during
|
||||||
|
// grammar development. Due to the performance overhead of calculating sets
|
||||||
|
// of ambiguous alternatives, this prediction mode should be avoided when
|
||||||
|
// the exact results are not necessary.</p>
|
||||||
|
//
|
||||||
|
// <p>
|
||||||
|
// This prediction mode does not provide any guarantees for prediction
|
||||||
|
// behavior for syntactically-incorrect inputs.</p>
|
||||||
|
//
|
||||||
|
PredictionModeLLExactAmbigDetection = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
// Computes the SLL prediction termination condition.
|
||||||
|
//
|
||||||
|
// <p>
|
||||||
|
// This method computes the SLL prediction termination condition for both of
|
||||||
|
// the following cases.</p>
|
||||||
|
//
|
||||||
|
// <ul>
|
||||||
|
// <li>The usual SLL+LL fallback upon SLL conflict</li>
|
||||||
|
// <li>Pure SLL without LL fallback</li>
|
||||||
|
// </ul>
|
||||||
|
//
|
||||||
|
// <p><strong>COMBINED SLL+LL PARSING</strong></p>
|
||||||
|
//
|
||||||
|
// <p>When LL-fallback is enabled upon SLL conflict, correct predictions are
|
||||||
|
// ensured regardless of how the termination condition is computed by this
|
||||||
|
// method. Due to the substantially higher cost of LL prediction, the
|
||||||
|
// prediction should only fall back to LL when the additional lookahead
|
||||||
|
// cannot lead to a unique SLL prediction.</p>
|
||||||
|
//
|
||||||
|
// <p>Assuming combined SLL+LL parsing, an SLL configuration set with only
|
||||||
|
// conflicting subsets should fall back to full LL, even if the
|
||||||
|
// configuration sets don't resolve to the same alternative (e.g.
|
||||||
|
// {@code {1,2}} and {@code {3,4}}. If there is at least one non-conflicting
|
||||||
|
// configuration, SLL could continue with the hopes that more lookahead will
|
||||||
|
// resolve via one of those non-conflicting configurations.</p>
|
||||||
|
//
|
||||||
|
// <p>Here's the prediction termination rule them: SLL (for SLL+LL parsing)
|
||||||
|
// stops when it sees only conflicting configuration subsets. In contrast,
|
||||||
|
// full LL keeps going when there is uncertainty.</p>
|
||||||
|
//
|
||||||
|
// <p><strong>HEURISTIC</strong></p>
|
||||||
|
//
|
||||||
|
// <p>As a heuristic, we stop prediction when we see any conflicting subset
|
||||||
|
// unless we see a state that only has one alternative associated with it.
|
||||||
|
// The single-alt-state thing lets prediction continue upon rules like
|
||||||
|
// (otherwise, it would admit defeat too soon):</p>
|
||||||
|
//
|
||||||
|
// <p>{@code [12|1|[], 6|2|[], 12|2|[]]. s : (ID | ID ID?) ” }</p>
|
||||||
|
//
|
||||||
|
// <p>When the ATN simulation reaches the state before {@code ”}, it has a
|
||||||
|
// DFA state that looks like: {@code [12|1|[], 6|2|[], 12|2|[]]}. Naturally
|
||||||
|
// {@code 12|1|[]} and {@code 12|2|[]} conflict, but we cannot stop
|
||||||
|
// processing this node because alternative to has another way to continue,
|
||||||
|
// via {@code [6|2|[]]}.</p>
|
||||||
|
//
|
||||||
|
// <p>It also let's us continue for this rule:</p>
|
||||||
|
//
|
||||||
|
// <p>{@code [1|1|[], 1|2|[], 8|3|[]] a : A | A | A B }</p>
|
||||||
|
//
|
||||||
|
// <p>After Matching input A, we reach the stop state for rule A, state 1.
|
||||||
|
// State 8 is the state right before B. Clearly alternatives 1 and 2
|
||||||
|
// conflict and no amount of further lookahead will separate the two.
|
||||||
|
// However, alternative 3 will be able to continue and so we do not stop
|
||||||
|
// working on this state. In the previous example, we're concerned with
|
||||||
|
// states associated with the conflicting alternatives. Here alt 3 is not
|
||||||
|
// associated with the conflicting configs, but since we can continue
|
||||||
|
// looking for input reasonably, don't declare the state done.</p>
|
||||||
|
//
|
||||||
|
// <p><strong>PURE SLL PARSING</strong></p>
|
||||||
|
//
|
||||||
|
// <p>To handle pure SLL parsing, all we have to do is make sure that we
|
||||||
|
// combine stack contexts for configurations that differ only by semantic
|
||||||
|
// predicate. From there, we can do the usual SLL termination heuristic.</p>
|
||||||
|
//
|
||||||
|
// <p><strong>PREDICATES IN SLL+LL PARSING</strong></p>
|
||||||
|
//
|
||||||
|
// <p>SLL decisions don't evaluate predicates until after they reach DFA stop
|
||||||
|
// states because they need to create the DFA cache that works in all
|
||||||
|
// semantic situations. In contrast, full LL evaluates predicates collected
|
||||||
|
// during start state computation so it can ignore predicates thereafter.
|
||||||
|
// This means that SLL termination detection can totally ignore semantic
|
||||||
|
// predicates.</p>
|
||||||
|
//
|
||||||
|
// <p>Implementation-wise, {@link ATNConfigSet} combines stack contexts but not
|
||||||
|
// semantic predicate contexts so we might see two configurations like the
|
||||||
|
// following.</p>
|
||||||
|
//
|
||||||
|
// <p>{@code (s, 1, x, {}), (s, 1, x', {p})}</p>
|
||||||
|
//
|
||||||
|
// <p>Before testing these configurations against others, we have to merge
|
||||||
|
// {@code x} and {@code x'} (without modifying the existing configurations).
|
||||||
|
// For example, we test {@code (x+x')==x”} when looking for conflicts in
|
||||||
|
// the following configurations.</p>
|
||||||
|
//
|
||||||
|
// <p>{@code (s, 1, x, {}), (s, 1, x', {p}), (s, 2, x”, {})}</p>
|
||||||
|
//
|
||||||
|
// <p>If the configuration set has predicates (as indicated by
|
||||||
|
// {@link ATNConfigSet//hasSemanticContext}), this algorithm makes a copy of
|
||||||
|
// the configurations to strip out all of the predicates so that a standard
|
||||||
|
// {@link ATNConfigSet} will merge everything ignoring predicates.</p>
|
||||||
|
func PredictionModehasSLLConflictTerminatingPrediction(mode int, configs ATNConfigSet) bool {
|
||||||
|
// Configs in rule stop states indicate reaching the end of the decision
|
||||||
|
// rule (local context) or end of start rule (full context). If all
|
||||||
|
// configs meet this condition, then none of the configurations is able
|
||||||
|
// to Match additional input so we terminate prediction.
|
||||||
|
//
|
||||||
|
if PredictionModeallConfigsInRuleStopStates(configs) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// pure SLL mode parsing
|
||||||
|
if mode == PredictionModeSLL {
|
||||||
|
// Don't bother with combining configs from different semantic
|
||||||
|
// contexts if we can fail over to full LL costs more time
|
||||||
|
// since we'll often fail over anyway.
|
||||||
|
if configs.HasSemanticContext() {
|
||||||
|
// dup configs, tossing out semantic predicates
|
||||||
|
dup := NewBaseATNConfigSet(false)
|
||||||
|
for _, c := range configs.GetItems() {
|
||||||
|
|
||||||
|
// NewBaseATNConfig({semanticContext:}, c)
|
||||||
|
c = NewBaseATNConfig2(c, SemanticContextNone)
|
||||||
|
dup.Add(c, nil)
|
||||||
|
}
|
||||||
|
configs = dup
|
||||||
|
}
|
||||||
|
// now we have combined contexts for configs with dissimilar preds
|
||||||
|
}
|
||||||
|
// pure SLL or combined SLL+LL mode parsing
|
||||||
|
altsets := PredictionModegetConflictingAltSubsets(configs)
|
||||||
|
return PredictionModehasConflictingAltSet(altsets) && !PredictionModehasStateAssociatedWithOneAlt(configs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if any configuration in {@code configs} is in a
|
||||||
|
// {@link RuleStopState}. Configurations meeting this condition have reached
|
||||||
|
// the end of the decision rule (local context) or end of start rule (full
|
||||||
|
// context).
|
||||||
|
//
|
||||||
|
// @param configs the configuration set to test
|
||||||
|
// @return {@code true} if any configuration in {@code configs} is in a
|
||||||
|
// {@link RuleStopState}, otherwise {@code false}
|
||||||
|
func PredictionModehasConfigInRuleStopState(configs ATNConfigSet) bool {
|
||||||
|
for _, c := range configs.GetItems() {
|
||||||
|
if _, ok := c.GetState().(*RuleStopState); ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if all configurations in {@code configs} are in a
|
||||||
|
// {@link RuleStopState}. Configurations meeting this condition have reached
|
||||||
|
// the end of the decision rule (local context) or end of start rule (full
|
||||||
|
// context).
|
||||||
|
//
|
||||||
|
// @param configs the configuration set to test
|
||||||
|
// @return {@code true} if all configurations in {@code configs} are in a
|
||||||
|
// {@link RuleStopState}, otherwise {@code false}
|
||||||
|
func PredictionModeallConfigsInRuleStopStates(configs ATNConfigSet) bool {
|
||||||
|
|
||||||
|
for _, c := range configs.GetItems() {
|
||||||
|
if _, ok := c.GetState().(*RuleStopState); !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Full LL prediction termination.
|
||||||
|
//
|
||||||
|
// <p>Can we stop looking ahead during ATN simulation or is there some
|
||||||
|
// uncertainty as to which alternative we will ultimately pick, after
|
||||||
|
// consuming more input? Even if there are partial conflicts, we might know
|
||||||
|
// that everything is going to resolve to the same minimum alternative. That
|
||||||
|
// means we can stop since no more lookahead will change that fact. On the
|
||||||
|
// other hand, there might be multiple conflicts that resolve to different
|
||||||
|
// minimums. That means we need more look ahead to decide which of those
|
||||||
|
// alternatives we should predict.</p>
|
||||||
|
//
|
||||||
|
// <p>The basic idea is to split the set of configurations {@code C}, into
|
||||||
|
// conflicting subsets {@code (s, _, ctx, _)} and singleton subsets with
|
||||||
|
// non-conflicting configurations. Two configurations conflict if they have
|
||||||
|
// identical {@link ATNConfig//state} and {@link ATNConfig//context} values
|
||||||
|
// but different {@link ATNConfig//alt} value, e.g. {@code (s, i, ctx, _)}
|
||||||
|
// and {@code (s, j, ctx, _)} for {@code i!=j}.</p>
|
||||||
|
//
|
||||||
|
// <p>Reduce these configuration subsets to the set of possible alternatives.
|
||||||
|
// You can compute the alternative subsets in one pass as follows:</p>
|
||||||
|
//
|
||||||
|
// <p>{@code A_s,ctx = {i | (s, i, ctx, _)}} for each configuration in
|
||||||
|
// {@code C} holding {@code s} and {@code ctx} fixed.</p>
|
||||||
|
//
|
||||||
|
// <p>Or in pseudo-code, for each configuration {@code c} in {@code C}:</p>
|
||||||
|
//
|
||||||
|
// <pre>
|
||||||
|
// map[c] U= c.{@link ATNConfig//alt alt} // map hash/equals uses s and x, not
|
||||||
|
// alt and not pred
|
||||||
|
// </pre>
|
||||||
|
//
|
||||||
|
// <p>The values in {@code map} are the set of {@code A_s,ctx} sets.</p>
|
||||||
|
//
|
||||||
|
// <p>If {@code |A_s,ctx|=1} then there is no conflict associated with
|
||||||
|
// {@code s} and {@code ctx}.</p>
|
||||||
|
//
|
||||||
|
// <p>Reduce the subsets to singletons by choosing a minimum of each subset. If
|
||||||
|
// the union of these alternative subsets is a singleton, then no amount of
|
||||||
|
// more lookahead will help us. We will always pick that alternative. If,
|
||||||
|
// however, there is more than one alternative, then we are uncertain which
|
||||||
|
// alternative to predict and must continue looking for resolution. We may
|
||||||
|
// or may not discover an ambiguity in the future, even if there are no
|
||||||
|
// conflicting subsets this round.</p>
|
||||||
|
//
|
||||||
|
// <p>The biggest sin is to terminate early because it means we've made a
|
||||||
|
// decision but were uncertain as to the eventual outcome. We haven't used
|
||||||
|
// enough lookahead. On the other hand, announcing a conflict too late is no
|
||||||
|
// big deal you will still have the conflict. It's just inefficient. It
|
||||||
|
// might even look until the end of file.</p>
|
||||||
|
//
|
||||||
|
// <p>No special consideration for semantic predicates is required because
|
||||||
|
// predicates are evaluated on-the-fly for full LL prediction, ensuring that
|
||||||
|
// no configuration contains a semantic context during the termination
|
||||||
|
// check.</p>
|
||||||
|
//
|
||||||
|
// <p><strong>CONFLICTING CONFIGS</strong></p>
|
||||||
|
//
|
||||||
|
// <p>Two configurations {@code (s, i, x)} and {@code (s, j, x')}, conflict
|
||||||
|
// when {@code i!=j} but {@code x=x'}. Because we merge all
|
||||||
|
// {@code (s, i, _)} configurations together, that means that there are at
|
||||||
|
// most {@code n} configurations associated with state {@code s} for
|
||||||
|
// {@code n} possible alternatives in the decision. The merged stacks
|
||||||
|
// complicate the comparison of configuration contexts {@code x} and
|
||||||
|
// {@code x'}. Sam checks to see if one is a subset of the other by calling
|
||||||
|
// merge and checking to see if the merged result is either {@code x} or
|
||||||
|
// {@code x'}. If the {@code x} associated with lowest alternative {@code i}
|
||||||
|
// is the superset, then {@code i} is the only possible prediction since the
|
||||||
|
// others resolve to {@code min(i)} as well. However, if {@code x} is
|
||||||
|
// associated with {@code j>i} then at least one stack configuration for
|
||||||
|
// {@code j} is not in conflict with alternative {@code i}. The algorithm
|
||||||
|
// should keep going, looking for more lookahead due to the uncertainty.</p>
|
||||||
|
//
|
||||||
|
// <p>For simplicity, I'm doing a equality check between {@code x} and
|
||||||
|
// {@code x'} that lets the algorithm continue to consume lookahead longer
|
||||||
|
// than necessary. The reason I like the equality is of course the
|
||||||
|
// simplicity but also because that is the test you need to detect the
|
||||||
|
// alternatives that are actually in conflict.</p>
|
||||||
|
//
|
||||||
|
// <p><strong>CONTINUE/STOP RULE</strong></p>
|
||||||
|
//
|
||||||
|
// <p>Continue if union of resolved alternative sets from non-conflicting and
|
||||||
|
// conflicting alternative subsets has more than one alternative. We are
|
||||||
|
// uncertain about which alternative to predict.</p>
|
||||||
|
//
|
||||||
|
// <p>The complete set of alternatives, {@code [i for (_,i,_)]}, tells us which
|
||||||
|
// alternatives are still in the running for the amount of input we've
|
||||||
|
// consumed at this point. The conflicting sets let us to strip away
|
||||||
|
// configurations that won't lead to more states because we resolve
|
||||||
|
// conflicts to the configuration with a minimum alternate for the
|
||||||
|
// conflicting set.</p>
|
||||||
|
//
|
||||||
|
// <p><strong>CASES</strong></p>
|
||||||
|
//
|
||||||
|
// <ul>
|
||||||
|
//
|
||||||
|
// <li>no conflicts and more than 1 alternative in set => continue</li>
|
||||||
|
//
|
||||||
|
// <li> {@code (s, 1, x)}, {@code (s, 2, x)}, {@code (s, 3, z)},
|
||||||
|
// {@code (s', 1, y)}, {@code (s', 2, y)} yields non-conflicting set
|
||||||
|
// {@code {3}} U conflicting sets {@code min({1,2})} U {@code min({1,2})} =
|
||||||
|
// {@code {1,3}} => continue
|
||||||
|
// </li>
|
||||||
|
//
|
||||||
|
// <li>{@code (s, 1, x)}, {@code (s, 2, x)}, {@code (s', 1, y)},
|
||||||
|
// {@code (s', 2, y)}, {@code (s”, 1, z)} yields non-conflicting set
|
||||||
|
// {@code {1}} U conflicting sets {@code min({1,2})} U {@code min({1,2})} =
|
||||||
|
// {@code {1}} => stop and predict 1</li>
|
||||||
|
//
|
||||||
|
// <li>{@code (s, 1, x)}, {@code (s, 2, x)}, {@code (s', 1, y)},
|
||||||
|
// {@code (s', 2, y)} yields conflicting, reduced sets {@code {1}} U
|
||||||
|
// {@code {1}} = {@code {1}} => stop and predict 1, can announce
|
||||||
|
// ambiguity {@code {1,2}}</li>
|
||||||
|
//
|
||||||
|
// <li>{@code (s, 1, x)}, {@code (s, 2, x)}, {@code (s', 2, y)},
|
||||||
|
// {@code (s', 3, y)} yields conflicting, reduced sets {@code {1}} U
|
||||||
|
// {@code {2}} = {@code {1,2}} => continue</li>
|
||||||
|
//
|
||||||
|
// <li>{@code (s, 1, x)}, {@code (s, 2, x)}, {@code (s', 3, y)},
|
||||||
|
// {@code (s', 4, y)} yields conflicting, reduced sets {@code {1}} U
|
||||||
|
// {@code {3}} = {@code {1,3}} => continue</li>
|
||||||
|
//
|
||||||
|
// </ul>
|
||||||
|
//
|
||||||
|
// <p><strong>EXACT AMBIGUITY DETECTION</strong></p>
|
||||||
|
//
|
||||||
|
// <p>If all states Report the same conflicting set of alternatives, then we
|
||||||
|
// know we have the exact ambiguity set.</p>
|
||||||
|
//
|
||||||
|
// <p><code>|A_<em>i</em>|>1</code> and
|
||||||
|
// <code>A_<em>i</em> = A_<em>j</em></code> for all <em>i</em>, <em>j</em>.</p>
|
||||||
|
//
|
||||||
|
// <p>In other words, we continue examining lookahead until all {@code A_i}
|
||||||
|
// have more than one alternative and all {@code A_i} are the same. If
|
||||||
|
// {@code A={{1,2}, {1,3}}}, then regular LL prediction would terminate
|
||||||
|
// because the resolved set is {@code {1}}. To determine what the real
|
||||||
|
// ambiguity is, we have to know whether the ambiguity is between one and
|
||||||
|
// two or one and three so we keep going. We can only stop prediction when
|
||||||
|
// we need exact ambiguity detection when the sets look like
|
||||||
|
// {@code A={{1,2}}} or {@code {{1,2},{1,2}}}, etc...</p>
|
||||||
|
func PredictionModeresolvesToJustOneViableAlt(altsets []*BitSet) int {
|
||||||
|
return PredictionModegetSingleViableAlt(altsets)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determines if every alternative subset in {@code altsets} contains more
|
||||||
|
// than one alternative.
|
||||||
|
//
|
||||||
|
// @param altsets a collection of alternative subsets
|
||||||
|
// @return {@code true} if every {@link BitSet} in {@code altsets} has
|
||||||
|
// {@link BitSet//cardinality cardinality} > 1, otherwise {@code false}
|
||||||
|
func PredictionModeallSubsetsConflict(altsets []*BitSet) bool {
|
||||||
|
return !PredictionModehasNonConflictingAltSet(altsets)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determines if any single alternative subset in {@code altsets} contains
|
||||||
|
// exactly one alternative.
|
||||||
|
//
|
||||||
|
// @param altsets a collection of alternative subsets
|
||||||
|
// @return {@code true} if {@code altsets} contains a {@link BitSet} with
|
||||||
|
// {@link BitSet//cardinality cardinality} 1, otherwise {@code false}
|
||||||
|
func PredictionModehasNonConflictingAltSet(altsets []*BitSet) bool {
|
||||||
|
for i := 0; i < len(altsets); i++ {
|
||||||
|
alts := altsets[i]
|
||||||
|
if alts.length() == 1 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determines if any single alternative subset in {@code altsets} contains
|
||||||
|
// more than one alternative.
|
||||||
|
//
|
||||||
|
// @param altsets a collection of alternative subsets
|
||||||
|
// @return {@code true} if {@code altsets} contains a {@link BitSet} with
|
||||||
|
// {@link BitSet//cardinality cardinality} > 1, otherwise {@code false}
|
||||||
|
func PredictionModehasConflictingAltSet(altsets []*BitSet) bool {
|
||||||
|
for i := 0; i < len(altsets); i++ {
|
||||||
|
alts := altsets[i]
|
||||||
|
if alts.length() > 1 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determines if every alternative subset in {@code altsets} is equivalent.
|
||||||
|
//
|
||||||
|
// @param altsets a collection of alternative subsets
|
||||||
|
// @return {@code true} if every member of {@code altsets} is equal to the
|
||||||
|
// others, otherwise {@code false}
|
||||||
|
func PredictionModeallSubsetsEqual(altsets []*BitSet) bool {
|
||||||
|
var first *BitSet
|
||||||
|
|
||||||
|
for i := 0; i < len(altsets); i++ {
|
||||||
|
alts := altsets[i]
|
||||||
|
if first == nil {
|
||||||
|
first = alts
|
||||||
|
} else if alts != first {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the unique alternative predicted by all alternative subsets in
|
||||||
|
// {@code altsets}. If no such alternative exists, this method returns
|
||||||
|
// {@link ATN//INVALID_ALT_NUMBER}.
|
||||||
|
//
|
||||||
|
// @param altsets a collection of alternative subsets
|
||||||
|
func PredictionModegetUniqueAlt(altsets []*BitSet) int {
|
||||||
|
all := PredictionModeGetAlts(altsets)
|
||||||
|
if all.length() == 1 {
|
||||||
|
return all.minValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
return ATNInvalidAltNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets the complete set of represented alternatives for a collection of
|
||||||
|
// alternative subsets. This method returns the union of each {@link BitSet}
|
||||||
|
// in {@code altsets}.
|
||||||
|
//
|
||||||
|
// @param altsets a collection of alternative subsets
|
||||||
|
// @return the set of represented alternatives in {@code altsets}
|
||||||
|
func PredictionModeGetAlts(altsets []*BitSet) *BitSet {
|
||||||
|
all := NewBitSet()
|
||||||
|
for _, alts := range altsets {
|
||||||
|
all.or(alts)
|
||||||
|
}
|
||||||
|
return all
|
||||||
|
}
|
||||||
|
|
||||||
|
// PredictionModegetConflictingAltSubsets gets the conflicting alt subsets from a configuration set.
|
||||||
|
// For each configuration {@code c} in {@code configs}:
|
||||||
|
//
|
||||||
|
// <pre>
|
||||||
|
// map[c] U= c.{@link ATNConfig//alt alt} // map hash/equals uses s and x, not
|
||||||
|
// alt and not pred
|
||||||
|
// </pre>
|
||||||
|
func PredictionModegetConflictingAltSubsets(configs ATNConfigSet) []*BitSet {
|
||||||
|
configToAlts := NewJMap[ATNConfig, *BitSet, *ATNAltConfigComparator[ATNConfig]](atnAltCfgEqInst)
|
||||||
|
|
||||||
|
for _, c := range configs.GetItems() {
|
||||||
|
|
||||||
|
alts, ok := configToAlts.Get(c)
|
||||||
|
if !ok {
|
||||||
|
alts = NewBitSet()
|
||||||
|
configToAlts.Put(c, alts)
|
||||||
|
}
|
||||||
|
alts.add(c.GetAlt())
|
||||||
|
}
|
||||||
|
|
||||||
|
return configToAlts.Values()
|
||||||
|
}
|
||||||
|
|
||||||
|
// PredictionModeGetStateToAltMap gets a map from state to alt subset from a configuration set. For each
|
||||||
|
// configuration {@code c} in {@code configs}:
|
||||||
|
//
|
||||||
|
// <pre>
|
||||||
|
// map[c.{@link ATNConfig//state state}] U= c.{@link ATNConfig//alt alt}
|
||||||
|
// </pre>
|
||||||
|
func PredictionModeGetStateToAltMap(configs ATNConfigSet) *AltDict {
|
||||||
|
m := NewAltDict()
|
||||||
|
|
||||||
|
for _, c := range configs.GetItems() {
|
||||||
|
alts := m.Get(c.GetState().String())
|
||||||
|
if alts == nil {
|
||||||
|
alts = NewBitSet()
|
||||||
|
m.put(c.GetState().String(), alts)
|
||||||
|
}
|
||||||
|
alts.(*BitSet).add(c.GetAlt())
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func PredictionModehasStateAssociatedWithOneAlt(configs ATNConfigSet) bool {
|
||||||
|
values := PredictionModeGetStateToAltMap(configs).values()
|
||||||
|
for i := 0; i < len(values); i++ {
|
||||||
|
if values[i].(*BitSet).length() == 1 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func PredictionModegetSingleViableAlt(altsets []*BitSet) int {
|
||||||
|
result := ATNInvalidAltNumber
|
||||||
|
|
||||||
|
for i := 0; i < len(altsets); i++ {
|
||||||
|
alts := altsets[i]
|
||||||
|
minAlt := alts.minValue()
|
||||||
|
if result == ATNInvalidAltNumber {
|
||||||
|
result = minAlt
|
||||||
|
} else if result != minAlt { // more than 1 viable alt
|
||||||
|
return ATNInvalidAltNumber
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
216
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/recognizer.go
generated
vendored
Normal file
216
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/recognizer.go
generated
vendored
Normal file
@@ -0,0 +1,216 @@
|
|||||||
|
// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
||||||
|
// Use of this file is governed by the BSD 3-clause license that
|
||||||
|
// can be found in the LICENSE.txt file in the project root.
|
||||||
|
|
||||||
|
package antlr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Recognizer interface {
|
||||||
|
GetLiteralNames() []string
|
||||||
|
GetSymbolicNames() []string
|
||||||
|
GetRuleNames() []string
|
||||||
|
|
||||||
|
Sempred(RuleContext, int, int) bool
|
||||||
|
Precpred(RuleContext, int) bool
|
||||||
|
|
||||||
|
GetState() int
|
||||||
|
SetState(int)
|
||||||
|
Action(RuleContext, int, int)
|
||||||
|
AddErrorListener(ErrorListener)
|
||||||
|
RemoveErrorListeners()
|
||||||
|
GetATN() *ATN
|
||||||
|
GetErrorListenerDispatch() ErrorListener
|
||||||
|
}
|
||||||
|
|
||||||
|
type BaseRecognizer struct {
|
||||||
|
listeners []ErrorListener
|
||||||
|
state int
|
||||||
|
|
||||||
|
RuleNames []string
|
||||||
|
LiteralNames []string
|
||||||
|
SymbolicNames []string
|
||||||
|
GrammarFileName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBaseRecognizer() *BaseRecognizer {
|
||||||
|
rec := new(BaseRecognizer)
|
||||||
|
rec.listeners = []ErrorListener{ConsoleErrorListenerINSTANCE}
|
||||||
|
rec.state = -1
|
||||||
|
return rec
|
||||||
|
}
|
||||||
|
|
||||||
|
var tokenTypeMapCache = make(map[string]int)
|
||||||
|
var ruleIndexMapCache = make(map[string]int)
|
||||||
|
|
||||||
|
func (b *BaseRecognizer) checkVersion(toolVersion string) {
|
||||||
|
runtimeVersion := "4.12.0"
|
||||||
|
if runtimeVersion != toolVersion {
|
||||||
|
fmt.Println("ANTLR runtime and generated code versions disagree: " + runtimeVersion + "!=" + toolVersion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseRecognizer) Action(context RuleContext, ruleIndex, actionIndex int) {
|
||||||
|
panic("action not implemented on Recognizer!")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseRecognizer) AddErrorListener(listener ErrorListener) {
|
||||||
|
b.listeners = append(b.listeners, listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseRecognizer) RemoveErrorListeners() {
|
||||||
|
b.listeners = make([]ErrorListener, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseRecognizer) GetRuleNames() []string {
|
||||||
|
return b.RuleNames
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseRecognizer) GetTokenNames() []string {
|
||||||
|
return b.LiteralNames
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseRecognizer) GetSymbolicNames() []string {
|
||||||
|
return b.SymbolicNames
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseRecognizer) GetLiteralNames() []string {
|
||||||
|
return b.LiteralNames
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseRecognizer) GetState() int {
|
||||||
|
return b.state
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseRecognizer) SetState(v int) {
|
||||||
|
b.state = v
|
||||||
|
}
|
||||||
|
|
||||||
|
//func (b *Recognizer) GetTokenTypeMap() {
|
||||||
|
// var tokenNames = b.GetTokenNames()
|
||||||
|
// if (tokenNames==nil) {
|
||||||
|
// panic("The current recognizer does not provide a list of token names.")
|
||||||
|
// }
|
||||||
|
// var result = tokenTypeMapCache[tokenNames]
|
||||||
|
// if(result==nil) {
|
||||||
|
// result = tokenNames.reduce(function(o, k, i) { o[k] = i })
|
||||||
|
// result.EOF = TokenEOF
|
||||||
|
// tokenTypeMapCache[tokenNames] = result
|
||||||
|
// }
|
||||||
|
// return result
|
||||||
|
//}
|
||||||
|
|
||||||
|
// Get a map from rule names to rule indexes.
|
||||||
|
//
|
||||||
|
// <p>Used for XPath and tree pattern compilation.</p>
|
||||||
|
func (b *BaseRecognizer) GetRuleIndexMap() map[string]int {
|
||||||
|
|
||||||
|
panic("Method not defined!")
|
||||||
|
// var ruleNames = b.GetRuleNames()
|
||||||
|
// if (ruleNames==nil) {
|
||||||
|
// panic("The current recognizer does not provide a list of rule names.")
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// var result = ruleIndexMapCache[ruleNames]
|
||||||
|
// if(result==nil) {
|
||||||
|
// result = ruleNames.reduce(function(o, k, i) { o[k] = i })
|
||||||
|
// ruleIndexMapCache[ruleNames] = result
|
||||||
|
// }
|
||||||
|
// return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseRecognizer) GetTokenType(tokenName string) int {
|
||||||
|
panic("Method not defined!")
|
||||||
|
// var ttype = b.GetTokenTypeMap()[tokenName]
|
||||||
|
// if (ttype !=nil) {
|
||||||
|
// return ttype
|
||||||
|
// } else {
|
||||||
|
// return TokenInvalidType
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
//func (b *Recognizer) GetTokenTypeMap() map[string]int {
|
||||||
|
// Vocabulary vocabulary = getVocabulary()
|
||||||
|
//
|
||||||
|
// Synchronized (tokenTypeMapCache) {
|
||||||
|
// Map<String, Integer> result = tokenTypeMapCache.Get(vocabulary)
|
||||||
|
// if (result == null) {
|
||||||
|
// result = new HashMap<String, Integer>()
|
||||||
|
// for (int i = 0; i < GetATN().maxTokenType; i++) {
|
||||||
|
// String literalName = vocabulary.getLiteralName(i)
|
||||||
|
// if (literalName != null) {
|
||||||
|
// result.put(literalName, i)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// String symbolicName = vocabulary.GetSymbolicName(i)
|
||||||
|
// if (symbolicName != null) {
|
||||||
|
// result.put(symbolicName, i)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// result.put("EOF", Token.EOF)
|
||||||
|
// result = Collections.unmodifiableMap(result)
|
||||||
|
// tokenTypeMapCache.put(vocabulary, result)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return result
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
// What is the error header, normally line/character position information?//
|
||||||
|
func (b *BaseRecognizer) GetErrorHeader(e RecognitionException) string {
|
||||||
|
line := e.GetOffendingToken().GetLine()
|
||||||
|
column := e.GetOffendingToken().GetColumn()
|
||||||
|
return "line " + strconv.Itoa(line) + ":" + strconv.Itoa(column)
|
||||||
|
}
|
||||||
|
|
||||||
|
// How should a token be displayed in an error message? The default
|
||||||
|
//
|
||||||
|
// is to display just the text, but during development you might
|
||||||
|
// want to have a lot of information spit out. Override in that case
|
||||||
|
// to use t.String() (which, for CommonToken, dumps everything about
|
||||||
|
// the token). This is better than forcing you to override a method in
|
||||||
|
// your token objects because you don't have to go modify your lexer
|
||||||
|
// so that it creates a NewJava type.
|
||||||
|
//
|
||||||
|
// @deprecated This method is not called by the ANTLR 4 Runtime. Specific
|
||||||
|
// implementations of {@link ANTLRErrorStrategy} may provide a similar
|
||||||
|
// feature when necessary. For example, see
|
||||||
|
// {@link DefaultErrorStrategy//GetTokenErrorDisplay}.
|
||||||
|
func (b *BaseRecognizer) GetTokenErrorDisplay(t Token) string {
|
||||||
|
if t == nil {
|
||||||
|
return "<no token>"
|
||||||
|
}
|
||||||
|
s := t.GetText()
|
||||||
|
if s == "" {
|
||||||
|
if t.GetTokenType() == TokenEOF {
|
||||||
|
s = "<EOF>"
|
||||||
|
} else {
|
||||||
|
s = "<" + strconv.Itoa(t.GetTokenType()) + ">"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s = strings.Replace(s, "\t", "\\t", -1)
|
||||||
|
s = strings.Replace(s, "\n", "\\n", -1)
|
||||||
|
s = strings.Replace(s, "\r", "\\r", -1)
|
||||||
|
|
||||||
|
return "'" + s + "'"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseRecognizer) GetErrorListenerDispatch() ErrorListener {
|
||||||
|
return NewProxyErrorListener(b.listeners)
|
||||||
|
}
|
||||||
|
|
||||||
|
// subclass needs to override these if there are sempreds or actions
|
||||||
|
// that the ATN interp needs to execute
|
||||||
|
func (b *BaseRecognizer) Sempred(localctx RuleContext, ruleIndex int, actionIndex int) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseRecognizer) Precpred(localctx RuleContext, precedence int) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
114
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/rule_context.go
generated
vendored
Normal file
114
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/rule_context.go
generated
vendored
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
||||||
|
// Use of this file is governed by the BSD 3-clause license that
|
||||||
|
// can be found in the LICENSE.txt file in the project root.
|
||||||
|
|
||||||
|
package antlr
|
||||||
|
|
||||||
|
// A rule context is a record of a single rule invocation. It knows
|
||||||
|
// which context invoked it, if any. If there is no parent context, then
|
||||||
|
// naturally the invoking state is not valid. The parent link
|
||||||
|
// provides a chain upwards from the current rule invocation to the root
|
||||||
|
// of the invocation tree, forming a stack. We actually carry no
|
||||||
|
// information about the rule associated with b context (except
|
||||||
|
// when parsing). We keep only the state number of the invoking state from
|
||||||
|
// the ATN submachine that invoked b. Contrast b with the s
|
||||||
|
// pointer inside ParserRuleContext that tracks the current state
|
||||||
|
// being "executed" for the current rule.
|
||||||
|
//
|
||||||
|
// The parent contexts are useful for computing lookahead sets and
|
||||||
|
// getting error information.
|
||||||
|
//
|
||||||
|
// These objects are used during parsing and prediction.
|
||||||
|
// For the special case of parsers, we use the subclass
|
||||||
|
// ParserRuleContext.
|
||||||
|
//
|
||||||
|
// @see ParserRuleContext
|
||||||
|
//
|
||||||
|
|
||||||
|
type RuleContext interface {
|
||||||
|
RuleNode
|
||||||
|
|
||||||
|
GetInvokingState() int
|
||||||
|
SetInvokingState(int)
|
||||||
|
|
||||||
|
GetRuleIndex() int
|
||||||
|
IsEmpty() bool
|
||||||
|
|
||||||
|
GetAltNumber() int
|
||||||
|
SetAltNumber(altNumber int)
|
||||||
|
|
||||||
|
String([]string, RuleContext) string
|
||||||
|
}
|
||||||
|
|
||||||
|
type BaseRuleContext struct {
|
||||||
|
parentCtx RuleContext
|
||||||
|
invokingState int
|
||||||
|
RuleIndex int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBaseRuleContext(parent RuleContext, invokingState int) *BaseRuleContext {
|
||||||
|
|
||||||
|
rn := new(BaseRuleContext)
|
||||||
|
|
||||||
|
// What context invoked b rule?
|
||||||
|
rn.parentCtx = parent
|
||||||
|
|
||||||
|
// What state invoked the rule associated with b context?
|
||||||
|
// The "return address" is the followState of invokingState
|
||||||
|
// If parent is nil, b should be -1.
|
||||||
|
if parent == nil {
|
||||||
|
rn.invokingState = -1
|
||||||
|
} else {
|
||||||
|
rn.invokingState = invokingState
|
||||||
|
}
|
||||||
|
|
||||||
|
return rn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseRuleContext) GetBaseRuleContext() *BaseRuleContext {
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseRuleContext) SetParent(v Tree) {
|
||||||
|
if v == nil {
|
||||||
|
b.parentCtx = nil
|
||||||
|
} else {
|
||||||
|
b.parentCtx = v.(RuleContext)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseRuleContext) GetInvokingState() int {
|
||||||
|
return b.invokingState
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseRuleContext) SetInvokingState(t int) {
|
||||||
|
b.invokingState = t
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseRuleContext) GetRuleIndex() int {
|
||||||
|
return b.RuleIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseRuleContext) GetAltNumber() int {
|
||||||
|
return ATNInvalidAltNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseRuleContext) SetAltNumber(altNumber int) {}
|
||||||
|
|
||||||
|
// A context is empty if there is no invoking state meaning nobody call
|
||||||
|
// current context.
|
||||||
|
func (b *BaseRuleContext) IsEmpty() bool {
|
||||||
|
return b.invokingState == -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the combined text of all child nodes. This method only considers
|
||||||
|
// tokens which have been added to the parse tree.
|
||||||
|
// <p>
|
||||||
|
// Since tokens on hidden channels (e.g. whitespace or comments) are not
|
||||||
|
// added to the parse trees, they will not appear in the output of b
|
||||||
|
// method.
|
||||||
|
//
|
||||||
|
|
||||||
|
func (b *BaseRuleContext) GetParent() Tree {
|
||||||
|
return b.parentCtx
|
||||||
|
}
|
||||||
469
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/semantic_context.go
generated
vendored
Normal file
469
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/semantic_context.go
generated
vendored
Normal file
@@ -0,0 +1,469 @@
|
|||||||
|
// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
||||||
|
// Use of this file is governed by the BSD 3-clause license that
|
||||||
|
// can be found in the LICENSE.txt file in the project root.
|
||||||
|
|
||||||
|
package antlr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A tree structure used to record the semantic context in which
|
||||||
|
// an ATN configuration is valid. It's either a single predicate,
|
||||||
|
// a conjunction {@code p1&&p2}, or a sum of products {@code p1||p2}.
|
||||||
|
//
|
||||||
|
// <p>I have scoped the {@link AND}, {@link OR}, and {@link Predicate} subclasses of
|
||||||
|
// {@link SemanticContext} within the scope of this outer class.</p>
|
||||||
|
//
|
||||||
|
|
||||||
|
type SemanticContext interface {
|
||||||
|
Equals(other Collectable[SemanticContext]) bool
|
||||||
|
Hash() int
|
||||||
|
|
||||||
|
evaluate(parser Recognizer, outerContext RuleContext) bool
|
||||||
|
evalPrecedence(parser Recognizer, outerContext RuleContext) SemanticContext
|
||||||
|
|
||||||
|
String() string
|
||||||
|
}
|
||||||
|
|
||||||
|
func SemanticContextandContext(a, b SemanticContext) SemanticContext {
|
||||||
|
if a == nil || a == SemanticContextNone {
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
if b == nil || b == SemanticContextNone {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
result := NewAND(a, b)
|
||||||
|
if len(result.opnds) == 1 {
|
||||||
|
return result.opnds[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func SemanticContextorContext(a, b SemanticContext) SemanticContext {
|
||||||
|
if a == nil {
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
if b == nil {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
if a == SemanticContextNone || b == SemanticContextNone {
|
||||||
|
return SemanticContextNone
|
||||||
|
}
|
||||||
|
result := NewOR(a, b)
|
||||||
|
if len(result.opnds) == 1 {
|
||||||
|
return result.opnds[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
type Predicate struct {
|
||||||
|
ruleIndex int
|
||||||
|
predIndex int
|
||||||
|
isCtxDependent bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPredicate(ruleIndex, predIndex int, isCtxDependent bool) *Predicate {
|
||||||
|
p := new(Predicate)
|
||||||
|
|
||||||
|
p.ruleIndex = ruleIndex
|
||||||
|
p.predIndex = predIndex
|
||||||
|
p.isCtxDependent = isCtxDependent // e.g., $i ref in pred
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
//The default {@link SemanticContext}, which is semantically equivalent to
|
||||||
|
//a predicate of the form {@code {true}?}.
|
||||||
|
|
||||||
|
var SemanticContextNone = NewPredicate(-1, -1, false)
|
||||||
|
|
||||||
|
func (p *Predicate) evalPrecedence(parser Recognizer, outerContext RuleContext) SemanticContext {
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Predicate) evaluate(parser Recognizer, outerContext RuleContext) bool {
|
||||||
|
|
||||||
|
var localctx RuleContext
|
||||||
|
|
||||||
|
if p.isCtxDependent {
|
||||||
|
localctx = outerContext
|
||||||
|
}
|
||||||
|
|
||||||
|
return parser.Sempred(localctx, p.ruleIndex, p.predIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Predicate) Equals(other Collectable[SemanticContext]) bool {
|
||||||
|
if p == other {
|
||||||
|
return true
|
||||||
|
} else if _, ok := other.(*Predicate); !ok {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
return p.ruleIndex == other.(*Predicate).ruleIndex &&
|
||||||
|
p.predIndex == other.(*Predicate).predIndex &&
|
||||||
|
p.isCtxDependent == other.(*Predicate).isCtxDependent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Predicate) Hash() int {
|
||||||
|
h := murmurInit(0)
|
||||||
|
h = murmurUpdate(h, p.ruleIndex)
|
||||||
|
h = murmurUpdate(h, p.predIndex)
|
||||||
|
if p.isCtxDependent {
|
||||||
|
h = murmurUpdate(h, 1)
|
||||||
|
} else {
|
||||||
|
h = murmurUpdate(h, 0)
|
||||||
|
}
|
||||||
|
return murmurFinish(h, 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Predicate) String() string {
|
||||||
|
return "{" + strconv.Itoa(p.ruleIndex) + ":" + strconv.Itoa(p.predIndex) + "}?"
|
||||||
|
}
|
||||||
|
|
||||||
|
type PrecedencePredicate struct {
|
||||||
|
precedence int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPrecedencePredicate(precedence int) *PrecedencePredicate {
|
||||||
|
|
||||||
|
p := new(PrecedencePredicate)
|
||||||
|
p.precedence = precedence
|
||||||
|
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PrecedencePredicate) evaluate(parser Recognizer, outerContext RuleContext) bool {
|
||||||
|
return parser.Precpred(outerContext, p.precedence)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PrecedencePredicate) evalPrecedence(parser Recognizer, outerContext RuleContext) SemanticContext {
|
||||||
|
if parser.Precpred(outerContext, p.precedence) {
|
||||||
|
return SemanticContextNone
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PrecedencePredicate) compareTo(other *PrecedencePredicate) int {
|
||||||
|
return p.precedence - other.precedence
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PrecedencePredicate) Equals(other Collectable[SemanticContext]) bool {
|
||||||
|
|
||||||
|
var op *PrecedencePredicate
|
||||||
|
var ok bool
|
||||||
|
if op, ok = other.(*PrecedencePredicate); !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if p == op {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.precedence == other.(*PrecedencePredicate).precedence
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PrecedencePredicate) Hash() int {
|
||||||
|
h := uint32(1)
|
||||||
|
h = 31*h + uint32(p.precedence)
|
||||||
|
return int(h)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PrecedencePredicate) String() string {
|
||||||
|
return "{" + strconv.Itoa(p.precedence) + ">=prec}?"
|
||||||
|
}
|
||||||
|
|
||||||
|
func PrecedencePredicatefilterPrecedencePredicates(set *JStore[SemanticContext, Comparator[SemanticContext]]) []*PrecedencePredicate {
|
||||||
|
result := make([]*PrecedencePredicate, 0)
|
||||||
|
|
||||||
|
set.Each(func(v SemanticContext) bool {
|
||||||
|
if c2, ok := v.(*PrecedencePredicate); ok {
|
||||||
|
result = append(result, c2)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// A semantic context which is true whenever none of the contained contexts
|
||||||
|
// is false.`
|
||||||
|
|
||||||
|
type AND struct {
|
||||||
|
opnds []SemanticContext
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAND(a, b SemanticContext) *AND {
|
||||||
|
|
||||||
|
operands := NewJStore[SemanticContext, Comparator[SemanticContext]](semctxEqInst)
|
||||||
|
if aa, ok := a.(*AND); ok {
|
||||||
|
for _, o := range aa.opnds {
|
||||||
|
operands.Put(o)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
operands.Put(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ba, ok := b.(*AND); ok {
|
||||||
|
for _, o := range ba.opnds {
|
||||||
|
operands.Put(o)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
operands.Put(b)
|
||||||
|
}
|
||||||
|
precedencePredicates := PrecedencePredicatefilterPrecedencePredicates(operands)
|
||||||
|
if len(precedencePredicates) > 0 {
|
||||||
|
// interested in the transition with the lowest precedence
|
||||||
|
var reduced *PrecedencePredicate
|
||||||
|
|
||||||
|
for _, p := range precedencePredicates {
|
||||||
|
if reduced == nil || p.precedence < reduced.precedence {
|
||||||
|
reduced = p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
operands.Put(reduced)
|
||||||
|
}
|
||||||
|
|
||||||
|
vs := operands.Values()
|
||||||
|
opnds := make([]SemanticContext, len(vs))
|
||||||
|
for i, v := range vs {
|
||||||
|
opnds[i] = v.(SemanticContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
and := new(AND)
|
||||||
|
and.opnds = opnds
|
||||||
|
|
||||||
|
return and
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AND) Equals(other Collectable[SemanticContext]) bool {
|
||||||
|
if a == other {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if _, ok := other.(*AND); !ok {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
for i, v := range other.(*AND).opnds {
|
||||||
|
if !a.opnds[i].Equals(v) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// {@inheritDoc}
|
||||||
|
//
|
||||||
|
// <p>
|
||||||
|
// The evaluation of predicates by a context is short-circuiting, but
|
||||||
|
// unordered.</p>
|
||||||
|
func (a *AND) evaluate(parser Recognizer, outerContext RuleContext) bool {
|
||||||
|
for i := 0; i < len(a.opnds); i++ {
|
||||||
|
if !a.opnds[i].evaluate(parser, outerContext) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AND) evalPrecedence(parser Recognizer, outerContext RuleContext) SemanticContext {
|
||||||
|
differs := false
|
||||||
|
operands := make([]SemanticContext, 0)
|
||||||
|
|
||||||
|
for i := 0; i < len(a.opnds); i++ {
|
||||||
|
context := a.opnds[i]
|
||||||
|
evaluated := context.evalPrecedence(parser, outerContext)
|
||||||
|
differs = differs || (evaluated != context)
|
||||||
|
if evaluated == nil {
|
||||||
|
// The AND context is false if any element is false
|
||||||
|
return nil
|
||||||
|
} else if evaluated != SemanticContextNone {
|
||||||
|
// Reduce the result by Skipping true elements
|
||||||
|
operands = append(operands, evaluated)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !differs {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(operands) == 0 {
|
||||||
|
// all elements were true, so the AND context is true
|
||||||
|
return SemanticContextNone
|
||||||
|
}
|
||||||
|
|
||||||
|
var result SemanticContext
|
||||||
|
|
||||||
|
for _, o := range operands {
|
||||||
|
if result == nil {
|
||||||
|
result = o
|
||||||
|
} else {
|
||||||
|
result = SemanticContextandContext(result, o)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AND) Hash() int {
|
||||||
|
h := murmurInit(37) // Init with a value different from OR
|
||||||
|
for _, op := range a.opnds {
|
||||||
|
h = murmurUpdate(h, op.Hash())
|
||||||
|
}
|
||||||
|
return murmurFinish(h, len(a.opnds))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *OR) Hash() int {
|
||||||
|
h := murmurInit(41) // Init with a value different from AND
|
||||||
|
for _, op := range a.opnds {
|
||||||
|
h = murmurUpdate(h, op.Hash())
|
||||||
|
}
|
||||||
|
return murmurFinish(h, len(a.opnds))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AND) String() string {
|
||||||
|
s := ""
|
||||||
|
|
||||||
|
for _, o := range a.opnds {
|
||||||
|
s += "&& " + fmt.Sprint(o)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(s) > 3 {
|
||||||
|
return s[0:3]
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// A semantic context which is true whenever at least one of the contained
|
||||||
|
// contexts is true.
|
||||||
|
//
|
||||||
|
|
||||||
|
type OR struct {
|
||||||
|
opnds []SemanticContext
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewOR(a, b SemanticContext) *OR {
|
||||||
|
|
||||||
|
operands := NewJStore[SemanticContext, Comparator[SemanticContext]](semctxEqInst)
|
||||||
|
if aa, ok := a.(*OR); ok {
|
||||||
|
for _, o := range aa.opnds {
|
||||||
|
operands.Put(o)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
operands.Put(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ba, ok := b.(*OR); ok {
|
||||||
|
for _, o := range ba.opnds {
|
||||||
|
operands.Put(o)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
operands.Put(b)
|
||||||
|
}
|
||||||
|
precedencePredicates := PrecedencePredicatefilterPrecedencePredicates(operands)
|
||||||
|
if len(precedencePredicates) > 0 {
|
||||||
|
// interested in the transition with the lowest precedence
|
||||||
|
var reduced *PrecedencePredicate
|
||||||
|
|
||||||
|
for _, p := range precedencePredicates {
|
||||||
|
if reduced == nil || p.precedence > reduced.precedence {
|
||||||
|
reduced = p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
operands.Put(reduced)
|
||||||
|
}
|
||||||
|
|
||||||
|
vs := operands.Values()
|
||||||
|
|
||||||
|
opnds := make([]SemanticContext, len(vs))
|
||||||
|
for i, v := range vs {
|
||||||
|
opnds[i] = v.(SemanticContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
o := new(OR)
|
||||||
|
o.opnds = opnds
|
||||||
|
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *OR) Equals(other Collectable[SemanticContext]) bool {
|
||||||
|
if o == other {
|
||||||
|
return true
|
||||||
|
} else if _, ok := other.(*OR); !ok {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
for i, v := range other.(*OR).opnds {
|
||||||
|
if !o.opnds[i].Equals(v) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// <p>
|
||||||
|
// The evaluation of predicates by o context is short-circuiting, but
|
||||||
|
// unordered.</p>
|
||||||
|
func (o *OR) evaluate(parser Recognizer, outerContext RuleContext) bool {
|
||||||
|
for i := 0; i < len(o.opnds); i++ {
|
||||||
|
if o.opnds[i].evaluate(parser, outerContext) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *OR) evalPrecedence(parser Recognizer, outerContext RuleContext) SemanticContext {
|
||||||
|
differs := false
|
||||||
|
operands := make([]SemanticContext, 0)
|
||||||
|
for i := 0; i < len(o.opnds); i++ {
|
||||||
|
context := o.opnds[i]
|
||||||
|
evaluated := context.evalPrecedence(parser, outerContext)
|
||||||
|
differs = differs || (evaluated != context)
|
||||||
|
if evaluated == SemanticContextNone {
|
||||||
|
// The OR context is true if any element is true
|
||||||
|
return SemanticContextNone
|
||||||
|
} else if evaluated != nil {
|
||||||
|
// Reduce the result by Skipping false elements
|
||||||
|
operands = append(operands, evaluated)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !differs {
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
if len(operands) == 0 {
|
||||||
|
// all elements were false, so the OR context is false
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var result SemanticContext
|
||||||
|
|
||||||
|
for _, o := range operands {
|
||||||
|
if result == nil {
|
||||||
|
result = o
|
||||||
|
} else {
|
||||||
|
result = SemanticContextorContext(result, o)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *OR) String() string {
|
||||||
|
s := ""
|
||||||
|
|
||||||
|
for _, o := range o.opnds {
|
||||||
|
s += "|| " + fmt.Sprint(o)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(s) > 3 {
|
||||||
|
return s[0:3]
|
||||||
|
}
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
209
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/token.go
generated
vendored
Normal file
209
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/token.go
generated
vendored
Normal file
@@ -0,0 +1,209 @@
|
|||||||
|
// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
||||||
|
// Use of this file is governed by the BSD 3-clause license that
|
||||||
|
// can be found in the LICENSE.txt file in the project root.
|
||||||
|
|
||||||
|
package antlr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TokenSourceCharStreamPair struct {
|
||||||
|
tokenSource TokenSource
|
||||||
|
charStream CharStream
|
||||||
|
}
|
||||||
|
|
||||||
|
// A token has properties: text, type, line, character position in the line
|
||||||
|
// (so we can ignore tabs), token channel, index, and source from which
|
||||||
|
// we obtained this token.
|
||||||
|
|
||||||
|
type Token interface {
|
||||||
|
GetSource() *TokenSourceCharStreamPair
|
||||||
|
GetTokenType() int
|
||||||
|
GetChannel() int
|
||||||
|
GetStart() int
|
||||||
|
GetStop() int
|
||||||
|
GetLine() int
|
||||||
|
GetColumn() int
|
||||||
|
|
||||||
|
GetText() string
|
||||||
|
SetText(s string)
|
||||||
|
|
||||||
|
GetTokenIndex() int
|
||||||
|
SetTokenIndex(v int)
|
||||||
|
|
||||||
|
GetTokenSource() TokenSource
|
||||||
|
GetInputStream() CharStream
|
||||||
|
}
|
||||||
|
|
||||||
|
type BaseToken struct {
|
||||||
|
source *TokenSourceCharStreamPair
|
||||||
|
tokenType int // token type of the token
|
||||||
|
channel int // The parser ignores everything not on DEFAULT_CHANNEL
|
||||||
|
start int // optional return -1 if not implemented.
|
||||||
|
stop int // optional return -1 if not implemented.
|
||||||
|
tokenIndex int // from 0..n-1 of the token object in the input stream
|
||||||
|
line int // line=1..n of the 1st character
|
||||||
|
column int // beginning of the line at which it occurs, 0..n-1
|
||||||
|
text string // text of the token.
|
||||||
|
readOnly bool
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
TokenInvalidType = 0
|
||||||
|
|
||||||
|
// During lookahead operations, this "token" signifies we hit rule end ATN state
|
||||||
|
// and did not follow it despite needing to.
|
||||||
|
TokenEpsilon = -2
|
||||||
|
|
||||||
|
TokenMinUserTokenType = 1
|
||||||
|
|
||||||
|
TokenEOF = -1
|
||||||
|
|
||||||
|
// All tokens go to the parser (unless Skip() is called in that rule)
|
||||||
|
// on a particular "channel". The parser tunes to a particular channel
|
||||||
|
// so that whitespace etc... can go to the parser on a "hidden" channel.
|
||||||
|
|
||||||
|
TokenDefaultChannel = 0
|
||||||
|
|
||||||
|
// Anything on different channel than DEFAULT_CHANNEL is not parsed
|
||||||
|
// by parser.
|
||||||
|
|
||||||
|
TokenHiddenChannel = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
func (b *BaseToken) GetChannel() int {
|
||||||
|
return b.channel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseToken) GetStart() int {
|
||||||
|
return b.start
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseToken) GetStop() int {
|
||||||
|
return b.stop
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseToken) GetLine() int {
|
||||||
|
return b.line
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseToken) GetColumn() int {
|
||||||
|
return b.column
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseToken) GetTokenType() int {
|
||||||
|
return b.tokenType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseToken) GetSource() *TokenSourceCharStreamPair {
|
||||||
|
return b.source
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseToken) GetTokenIndex() int {
|
||||||
|
return b.tokenIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseToken) SetTokenIndex(v int) {
|
||||||
|
b.tokenIndex = v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseToken) GetTokenSource() TokenSource {
|
||||||
|
return b.source.tokenSource
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseToken) GetInputStream() CharStream {
|
||||||
|
return b.source.charStream
|
||||||
|
}
|
||||||
|
|
||||||
|
type CommonToken struct {
|
||||||
|
*BaseToken
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCommonToken(source *TokenSourceCharStreamPair, tokenType, channel, start, stop int) *CommonToken {
|
||||||
|
|
||||||
|
t := new(CommonToken)
|
||||||
|
|
||||||
|
t.BaseToken = new(BaseToken)
|
||||||
|
|
||||||
|
t.source = source
|
||||||
|
t.tokenType = tokenType
|
||||||
|
t.channel = channel
|
||||||
|
t.start = start
|
||||||
|
t.stop = stop
|
||||||
|
t.tokenIndex = -1
|
||||||
|
if t.source.tokenSource != nil {
|
||||||
|
t.line = source.tokenSource.GetLine()
|
||||||
|
t.column = source.tokenSource.GetCharPositionInLine()
|
||||||
|
} else {
|
||||||
|
t.column = -1
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
// An empty {@link Pair} which is used as the default value of
|
||||||
|
// {@link //source} for tokens that do not have a source.
|
||||||
|
|
||||||
|
//CommonToken.EMPTY_SOURCE = [ nil, nil ]
|
||||||
|
|
||||||
|
// Constructs a New{@link CommonToken} as a copy of another {@link Token}.
|
||||||
|
//
|
||||||
|
// <p>
|
||||||
|
// If {@code oldToken} is also a {@link CommonToken} instance, the newly
|
||||||
|
// constructed token will share a reference to the {@link //text} field and
|
||||||
|
// the {@link Pair} stored in {@link //source}. Otherwise, {@link //text} will
|
||||||
|
// be assigned the result of calling {@link //GetText}, and {@link //source}
|
||||||
|
// will be constructed from the result of {@link Token//GetTokenSource} and
|
||||||
|
// {@link Token//GetInputStream}.</p>
|
||||||
|
//
|
||||||
|
// @param oldToken The token to copy.
|
||||||
|
func (c *CommonToken) clone() *CommonToken {
|
||||||
|
t := NewCommonToken(c.source, c.tokenType, c.channel, c.start, c.stop)
|
||||||
|
t.tokenIndex = c.GetTokenIndex()
|
||||||
|
t.line = c.GetLine()
|
||||||
|
t.column = c.GetColumn()
|
||||||
|
t.text = c.GetText()
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommonToken) GetText() string {
|
||||||
|
if c.text != "" {
|
||||||
|
return c.text
|
||||||
|
}
|
||||||
|
input := c.GetInputStream()
|
||||||
|
if input == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
n := input.Size()
|
||||||
|
if c.start < n && c.stop < n {
|
||||||
|
return input.GetTextFromInterval(NewInterval(c.start, c.stop))
|
||||||
|
}
|
||||||
|
return "<EOF>"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommonToken) SetText(text string) {
|
||||||
|
c.text = text
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CommonToken) String() string {
|
||||||
|
txt := c.GetText()
|
||||||
|
if txt != "" {
|
||||||
|
txt = strings.Replace(txt, "\n", "\\n", -1)
|
||||||
|
txt = strings.Replace(txt, "\r", "\\r", -1)
|
||||||
|
txt = strings.Replace(txt, "\t", "\\t", -1)
|
||||||
|
} else {
|
||||||
|
txt = "<no text>"
|
||||||
|
}
|
||||||
|
|
||||||
|
var ch string
|
||||||
|
if c.channel > 0 {
|
||||||
|
ch = ",channel=" + strconv.Itoa(c.channel)
|
||||||
|
} else {
|
||||||
|
ch = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return "[@" + strconv.Itoa(c.tokenIndex) + "," + strconv.Itoa(c.start) + ":" + strconv.Itoa(c.stop) + "='" +
|
||||||
|
txt + "',<" + strconv.Itoa(c.tokenType) + ">" +
|
||||||
|
ch + "," + strconv.Itoa(c.line) + ":" + strconv.Itoa(c.column) + "]"
|
||||||
|
}
|
||||||
17
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/token_source.go
generated
vendored
Normal file
17
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/token_source.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
||||||
|
// Use of this file is governed by the BSD 3-clause license that
|
||||||
|
// can be found in the LICENSE.txt file in the project root.
|
||||||
|
|
||||||
|
package antlr
|
||||||
|
|
||||||
|
type TokenSource interface {
|
||||||
|
NextToken() Token
|
||||||
|
Skip()
|
||||||
|
More()
|
||||||
|
GetLine() int
|
||||||
|
GetCharPositionInLine() int
|
||||||
|
GetInputStream() CharStream
|
||||||
|
GetSourceName() string
|
||||||
|
setTokenFactory(factory TokenFactory)
|
||||||
|
GetTokenFactory() TokenFactory
|
||||||
|
}
|
||||||
20
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/token_stream.go
generated
vendored
Normal file
20
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/token_stream.go
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
||||||
|
// Use of this file is governed by the BSD 3-clause license that
|
||||||
|
// can be found in the LICENSE.txt file in the project root.
|
||||||
|
|
||||||
|
package antlr
|
||||||
|
|
||||||
|
type TokenStream interface {
|
||||||
|
IntStream
|
||||||
|
|
||||||
|
LT(k int) Token
|
||||||
|
|
||||||
|
Get(index int) Token
|
||||||
|
GetTokenSource() TokenSource
|
||||||
|
SetTokenSource(TokenSource)
|
||||||
|
|
||||||
|
GetAllText() string
|
||||||
|
GetTextFromInterval(*Interval) string
|
||||||
|
GetTextFromRuleContext(RuleContext) string
|
||||||
|
GetTextFromTokens(Token, Token) string
|
||||||
|
}
|
||||||
659
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/tokenstream_rewriter.go
generated
vendored
Normal file
659
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/tokenstream_rewriter.go
generated
vendored
Normal file
@@ -0,0 +1,659 @@
|
|||||||
|
// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
||||||
|
// Use of this file is governed by the BSD 3-clause license that
|
||||||
|
// can be found in the LICENSE.txt file in the project root.
|
||||||
|
|
||||||
|
package antlr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
//
|
||||||
|
// Useful for rewriting out a buffered input token stream after doing some
|
||||||
|
// augmentation or other manipulations on it.
|
||||||
|
|
||||||
|
// <p>
|
||||||
|
// You can insert stuff, replace, and delete chunks. Note that the operations
|
||||||
|
// are done lazily--only if you convert the buffer to a {@link String} with
|
||||||
|
// {@link TokenStream#getText()}. This is very efficient because you are not
|
||||||
|
// moving data around all the time. As the buffer of tokens is converted to
|
||||||
|
// strings, the {@link #getText()} method(s) scan the input token stream and
|
||||||
|
// check to see if there is an operation at the current index. If so, the
|
||||||
|
// operation is done and then normal {@link String} rendering continues on the
|
||||||
|
// buffer. This is like having multiple Turing machine instruction streams
|
||||||
|
// (programs) operating on a single input tape. :)</p>
|
||||||
|
// <p>
|
||||||
|
|
||||||
|
// This rewriter makes no modifications to the token stream. It does not ask the
|
||||||
|
// stream to fill itself up nor does it advance the input cursor. The token
|
||||||
|
// stream {@link TokenStream#index()} will return the same value before and
|
||||||
|
// after any {@link #getText()} call.</p>
|
||||||
|
|
||||||
|
// <p>
|
||||||
|
// The rewriter only works on tokens that you have in the buffer and ignores the
|
||||||
|
// current input cursor. If you are buffering tokens on-demand, calling
|
||||||
|
// {@link #getText()} halfway through the input will only do rewrites for those
|
||||||
|
// tokens in the first half of the file.</p>
|
||||||
|
|
||||||
|
// <p>
|
||||||
|
// Since the operations are done lazily at {@link #getText}-time, operations do
|
||||||
|
// not screw up the token index values. That is, an insert operation at token
|
||||||
|
// index {@code i} does not change the index values for tokens
|
||||||
|
// {@code i}+1..n-1.</p>
|
||||||
|
|
||||||
|
// <p>
|
||||||
|
// Because operations never actually alter the buffer, you may always get the
|
||||||
|
// original token stream back without undoing anything. Since the instructions
|
||||||
|
// are queued up, you can easily simulate transactions and roll back any changes
|
||||||
|
// if there is an error just by removing instructions. For example,</p>
|
||||||
|
|
||||||
|
// <pre>
|
||||||
|
// CharStream input = new ANTLRFileStream("input");
|
||||||
|
// TLexer lex = new TLexer(input);
|
||||||
|
// CommonTokenStream tokens = new CommonTokenStream(lex);
|
||||||
|
// T parser = new T(tokens);
|
||||||
|
// TokenStreamRewriter rewriter = new TokenStreamRewriter(tokens);
|
||||||
|
// parser.startRule();
|
||||||
|
// </pre>
|
||||||
|
|
||||||
|
// <p>
|
||||||
|
// Then in the rules, you can execute (assuming rewriter is visible):</p>
|
||||||
|
|
||||||
|
// <pre>
|
||||||
|
// Token t,u;
|
||||||
|
// ...
|
||||||
|
// rewriter.insertAfter(t, "text to put after t");}
|
||||||
|
// rewriter.insertAfter(u, "text after u");}
|
||||||
|
// System.out.println(rewriter.getText());
|
||||||
|
// </pre>
|
||||||
|
|
||||||
|
// <p>
|
||||||
|
// You can also have multiple "instruction streams" and get multiple rewrites
|
||||||
|
// from a single pass over the input. Just name the instruction streams and use
|
||||||
|
// that name again when printing the buffer. This could be useful for generating
|
||||||
|
// a C file and also its header file--all from the same buffer:</p>
|
||||||
|
|
||||||
|
// <pre>
|
||||||
|
// rewriter.insertAfter("pass1", t, "text to put after t");}
|
||||||
|
// rewriter.insertAfter("pass2", u, "text after u");}
|
||||||
|
// System.out.println(rewriter.getText("pass1"));
|
||||||
|
// System.out.println(rewriter.getText("pass2"));
|
||||||
|
// </pre>
|
||||||
|
|
||||||
|
// <p>
|
||||||
|
// If you don't use named rewrite streams, a "default" stream is used as the
|
||||||
|
// first example shows.</p>
|
||||||
|
|
||||||
|
const (
|
||||||
|
Default_Program_Name = "default"
|
||||||
|
Program_Init_Size = 100
|
||||||
|
Min_Token_Index = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
// Define the rewrite operation hierarchy
|
||||||
|
|
||||||
|
type RewriteOperation interface {
|
||||||
|
// Execute the rewrite operation by possibly adding to the buffer.
|
||||||
|
// Return the index of the next token to operate on.
|
||||||
|
Execute(buffer *bytes.Buffer) int
|
||||||
|
String() string
|
||||||
|
GetInstructionIndex() int
|
||||||
|
GetIndex() int
|
||||||
|
GetText() string
|
||||||
|
GetOpName() string
|
||||||
|
GetTokens() TokenStream
|
||||||
|
SetInstructionIndex(val int)
|
||||||
|
SetIndex(int)
|
||||||
|
SetText(string)
|
||||||
|
SetOpName(string)
|
||||||
|
SetTokens(TokenStream)
|
||||||
|
}
|
||||||
|
|
||||||
|
type BaseRewriteOperation struct {
|
||||||
|
//Current index of rewrites list
|
||||||
|
instruction_index int
|
||||||
|
//Token buffer index
|
||||||
|
index int
|
||||||
|
//Substitution text
|
||||||
|
text string
|
||||||
|
//Actual operation name
|
||||||
|
op_name string
|
||||||
|
//Pointer to token steam
|
||||||
|
tokens TokenStream
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op *BaseRewriteOperation) GetInstructionIndex() int {
|
||||||
|
return op.instruction_index
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op *BaseRewriteOperation) GetIndex() int {
|
||||||
|
return op.index
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op *BaseRewriteOperation) GetText() string {
|
||||||
|
return op.text
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op *BaseRewriteOperation) GetOpName() string {
|
||||||
|
return op.op_name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op *BaseRewriteOperation) GetTokens() TokenStream {
|
||||||
|
return op.tokens
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op *BaseRewriteOperation) SetInstructionIndex(val int) {
|
||||||
|
op.instruction_index = val
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op *BaseRewriteOperation) SetIndex(val int) {
|
||||||
|
op.index = val
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op *BaseRewriteOperation) SetText(val string) {
|
||||||
|
op.text = val
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op *BaseRewriteOperation) SetOpName(val string) {
|
||||||
|
op.op_name = val
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op *BaseRewriteOperation) SetTokens(val TokenStream) {
|
||||||
|
op.tokens = val
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op *BaseRewriteOperation) Execute(buffer *bytes.Buffer) int {
|
||||||
|
return op.index
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op *BaseRewriteOperation) String() string {
|
||||||
|
return fmt.Sprintf("<%s@%d:\"%s\">",
|
||||||
|
op.op_name,
|
||||||
|
op.tokens.Get(op.GetIndex()),
|
||||||
|
op.text,
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
type InsertBeforeOp struct {
|
||||||
|
BaseRewriteOperation
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewInsertBeforeOp(index int, text string, stream TokenStream) *InsertBeforeOp {
|
||||||
|
return &InsertBeforeOp{BaseRewriteOperation: BaseRewriteOperation{
|
||||||
|
index: index,
|
||||||
|
text: text,
|
||||||
|
op_name: "InsertBeforeOp",
|
||||||
|
tokens: stream,
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op *InsertBeforeOp) Execute(buffer *bytes.Buffer) int {
|
||||||
|
buffer.WriteString(op.text)
|
||||||
|
if op.tokens.Get(op.index).GetTokenType() != TokenEOF {
|
||||||
|
buffer.WriteString(op.tokens.Get(op.index).GetText())
|
||||||
|
}
|
||||||
|
return op.index + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op *InsertBeforeOp) String() string {
|
||||||
|
return op.BaseRewriteOperation.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Distinguish between insert after/before to do the "insert afters"
|
||||||
|
// first and then the "insert befores" at same index. Implementation
|
||||||
|
// of "insert after" is "insert before index+1".
|
||||||
|
|
||||||
|
type InsertAfterOp struct {
|
||||||
|
BaseRewriteOperation
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewInsertAfterOp(index int, text string, stream TokenStream) *InsertAfterOp {
|
||||||
|
return &InsertAfterOp{BaseRewriteOperation: BaseRewriteOperation{
|
||||||
|
index: index + 1,
|
||||||
|
text: text,
|
||||||
|
tokens: stream,
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op *InsertAfterOp) Execute(buffer *bytes.Buffer) int {
|
||||||
|
buffer.WriteString(op.text)
|
||||||
|
if op.tokens.Get(op.index).GetTokenType() != TokenEOF {
|
||||||
|
buffer.WriteString(op.tokens.Get(op.index).GetText())
|
||||||
|
}
|
||||||
|
return op.index + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op *InsertAfterOp) String() string {
|
||||||
|
return op.BaseRewriteOperation.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// I'm going to try replacing range from x..y with (y-x)+1 ReplaceOp
|
||||||
|
// instructions.
|
||||||
|
type ReplaceOp struct {
|
||||||
|
BaseRewriteOperation
|
||||||
|
LastIndex int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewReplaceOp(from, to int, text string, stream TokenStream) *ReplaceOp {
|
||||||
|
return &ReplaceOp{
|
||||||
|
BaseRewriteOperation: BaseRewriteOperation{
|
||||||
|
index: from,
|
||||||
|
text: text,
|
||||||
|
op_name: "ReplaceOp",
|
||||||
|
tokens: stream,
|
||||||
|
},
|
||||||
|
LastIndex: to,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op *ReplaceOp) Execute(buffer *bytes.Buffer) int {
|
||||||
|
if op.text != "" {
|
||||||
|
buffer.WriteString(op.text)
|
||||||
|
}
|
||||||
|
return op.LastIndex + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op *ReplaceOp) String() string {
|
||||||
|
if op.text == "" {
|
||||||
|
return fmt.Sprintf("<DeleteOP@%d..%d>",
|
||||||
|
op.tokens.Get(op.index), op.tokens.Get(op.LastIndex))
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("<ReplaceOp@%d..%d:\"%s\">",
|
||||||
|
op.tokens.Get(op.index), op.tokens.Get(op.LastIndex), op.text)
|
||||||
|
}
|
||||||
|
|
||||||
|
type TokenStreamRewriter struct {
|
||||||
|
//Our source stream
|
||||||
|
tokens TokenStream
|
||||||
|
// You may have multiple, named streams of rewrite operations.
|
||||||
|
// I'm calling these things "programs."
|
||||||
|
// Maps String (name) → rewrite (List)
|
||||||
|
programs map[string][]RewriteOperation
|
||||||
|
last_rewrite_token_indexes map[string]int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTokenStreamRewriter(tokens TokenStream) *TokenStreamRewriter {
|
||||||
|
return &TokenStreamRewriter{
|
||||||
|
tokens: tokens,
|
||||||
|
programs: map[string][]RewriteOperation{
|
||||||
|
Default_Program_Name: make([]RewriteOperation, 0, Program_Init_Size),
|
||||||
|
},
|
||||||
|
last_rewrite_token_indexes: map[string]int{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tsr *TokenStreamRewriter) GetTokenStream() TokenStream {
|
||||||
|
return tsr.tokens
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rollback the instruction stream for a program so that
|
||||||
|
// the indicated instruction (via instructionIndex) is no
|
||||||
|
// longer in the stream. UNTESTED!
|
||||||
|
func (tsr *TokenStreamRewriter) Rollback(program_name string, instruction_index int) {
|
||||||
|
is, ok := tsr.programs[program_name]
|
||||||
|
if ok {
|
||||||
|
tsr.programs[program_name] = is[Min_Token_Index:instruction_index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tsr *TokenStreamRewriter) RollbackDefault(instruction_index int) {
|
||||||
|
tsr.Rollback(Default_Program_Name, instruction_index)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset the program so that no instructions exist
|
||||||
|
func (tsr *TokenStreamRewriter) DeleteProgram(program_name string) {
|
||||||
|
tsr.Rollback(program_name, Min_Token_Index) //TODO: double test on that cause lower bound is not included
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tsr *TokenStreamRewriter) DeleteProgramDefault() {
|
||||||
|
tsr.DeleteProgram(Default_Program_Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tsr *TokenStreamRewriter) InsertAfter(program_name string, index int, text string) {
|
||||||
|
// to insert after, just insert before next index (even if past end)
|
||||||
|
var op RewriteOperation = NewInsertAfterOp(index, text, tsr.tokens)
|
||||||
|
rewrites := tsr.GetProgram(program_name)
|
||||||
|
op.SetInstructionIndex(len(rewrites))
|
||||||
|
tsr.AddToProgram(program_name, op)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tsr *TokenStreamRewriter) InsertAfterDefault(index int, text string) {
|
||||||
|
tsr.InsertAfter(Default_Program_Name, index, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tsr *TokenStreamRewriter) InsertAfterToken(program_name string, token Token, text string) {
|
||||||
|
tsr.InsertAfter(program_name, token.GetTokenIndex(), text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tsr *TokenStreamRewriter) InsertBefore(program_name string, index int, text string) {
|
||||||
|
var op RewriteOperation = NewInsertBeforeOp(index, text, tsr.tokens)
|
||||||
|
rewrites := tsr.GetProgram(program_name)
|
||||||
|
op.SetInstructionIndex(len(rewrites))
|
||||||
|
tsr.AddToProgram(program_name, op)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tsr *TokenStreamRewriter) InsertBeforeDefault(index int, text string) {
|
||||||
|
tsr.InsertBefore(Default_Program_Name, index, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tsr *TokenStreamRewriter) InsertBeforeToken(program_name string, token Token, text string) {
|
||||||
|
tsr.InsertBefore(program_name, token.GetTokenIndex(), text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tsr *TokenStreamRewriter) Replace(program_name string, from, to int, text string) {
|
||||||
|
if from > to || from < 0 || to < 0 || to >= tsr.tokens.Size() {
|
||||||
|
panic(fmt.Sprintf("replace: range invalid: %d..%d(size=%d)",
|
||||||
|
from, to, tsr.tokens.Size()))
|
||||||
|
}
|
||||||
|
var op RewriteOperation = NewReplaceOp(from, to, text, tsr.tokens)
|
||||||
|
rewrites := tsr.GetProgram(program_name)
|
||||||
|
op.SetInstructionIndex(len(rewrites))
|
||||||
|
tsr.AddToProgram(program_name, op)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tsr *TokenStreamRewriter) ReplaceDefault(from, to int, text string) {
|
||||||
|
tsr.Replace(Default_Program_Name, from, to, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tsr *TokenStreamRewriter) ReplaceDefaultPos(index int, text string) {
|
||||||
|
tsr.ReplaceDefault(index, index, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tsr *TokenStreamRewriter) ReplaceToken(program_name string, from, to Token, text string) {
|
||||||
|
tsr.Replace(program_name, from.GetTokenIndex(), to.GetTokenIndex(), text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tsr *TokenStreamRewriter) ReplaceTokenDefault(from, to Token, text string) {
|
||||||
|
tsr.ReplaceToken(Default_Program_Name, from, to, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tsr *TokenStreamRewriter) ReplaceTokenDefaultPos(index Token, text string) {
|
||||||
|
tsr.ReplaceTokenDefault(index, index, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tsr *TokenStreamRewriter) Delete(program_name string, from, to int) {
|
||||||
|
tsr.Replace(program_name, from, to, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tsr *TokenStreamRewriter) DeleteDefault(from, to int) {
|
||||||
|
tsr.Delete(Default_Program_Name, from, to)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tsr *TokenStreamRewriter) DeleteDefaultPos(index int) {
|
||||||
|
tsr.DeleteDefault(index, index)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tsr *TokenStreamRewriter) DeleteToken(program_name string, from, to Token) {
|
||||||
|
tsr.ReplaceToken(program_name, from, to, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tsr *TokenStreamRewriter) DeleteTokenDefault(from, to Token) {
|
||||||
|
tsr.DeleteToken(Default_Program_Name, from, to)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tsr *TokenStreamRewriter) GetLastRewriteTokenIndex(program_name string) int {
|
||||||
|
i, ok := tsr.last_rewrite_token_indexes[program_name]
|
||||||
|
if !ok {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tsr *TokenStreamRewriter) GetLastRewriteTokenIndexDefault() int {
|
||||||
|
return tsr.GetLastRewriteTokenIndex(Default_Program_Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tsr *TokenStreamRewriter) SetLastRewriteTokenIndex(program_name string, i int) {
|
||||||
|
tsr.last_rewrite_token_indexes[program_name] = i
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tsr *TokenStreamRewriter) InitializeProgram(name string) []RewriteOperation {
|
||||||
|
is := make([]RewriteOperation, 0, Program_Init_Size)
|
||||||
|
tsr.programs[name] = is
|
||||||
|
return is
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tsr *TokenStreamRewriter) AddToProgram(name string, op RewriteOperation) {
|
||||||
|
is := tsr.GetProgram(name)
|
||||||
|
is = append(is, op)
|
||||||
|
tsr.programs[name] = is
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tsr *TokenStreamRewriter) GetProgram(name string) []RewriteOperation {
|
||||||
|
is, ok := tsr.programs[name]
|
||||||
|
if !ok {
|
||||||
|
is = tsr.InitializeProgram(name)
|
||||||
|
}
|
||||||
|
return is
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the text from the original tokens altered per the
|
||||||
|
// instructions given to this rewriter.
|
||||||
|
func (tsr *TokenStreamRewriter) GetTextDefault() string {
|
||||||
|
return tsr.GetText(
|
||||||
|
Default_Program_Name,
|
||||||
|
NewInterval(0, tsr.tokens.Size()-1))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the text from the original tokens altered per the
|
||||||
|
// instructions given to this rewriter.
|
||||||
|
func (tsr *TokenStreamRewriter) GetText(program_name string, interval *Interval) string {
|
||||||
|
rewrites := tsr.programs[program_name]
|
||||||
|
start := interval.Start
|
||||||
|
stop := interval.Stop
|
||||||
|
// ensure start/end are in range
|
||||||
|
stop = min(stop, tsr.tokens.Size()-1)
|
||||||
|
start = max(start, 0)
|
||||||
|
if rewrites == nil || len(rewrites) == 0 {
|
||||||
|
return tsr.tokens.GetTextFromInterval(interval) // no instructions to execute
|
||||||
|
}
|
||||||
|
buf := bytes.Buffer{}
|
||||||
|
// First, optimize instruction stream
|
||||||
|
indexToOp := reduceToSingleOperationPerIndex(rewrites)
|
||||||
|
// Walk buffer, executing instructions and emitting tokens
|
||||||
|
for i := start; i <= stop && i < tsr.tokens.Size(); {
|
||||||
|
op := indexToOp[i]
|
||||||
|
delete(indexToOp, i) // remove so any left have index size-1
|
||||||
|
t := tsr.tokens.Get(i)
|
||||||
|
if op == nil {
|
||||||
|
// no operation at that index, just dump token
|
||||||
|
if t.GetTokenType() != TokenEOF {
|
||||||
|
buf.WriteString(t.GetText())
|
||||||
|
}
|
||||||
|
i++ // move to next token
|
||||||
|
} else {
|
||||||
|
i = op.Execute(&buf) // execute operation and skip
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// include stuff after end if it's last index in buffer
|
||||||
|
// So, if they did an insertAfter(lastValidIndex, "foo"), include
|
||||||
|
// foo if end==lastValidIndex.
|
||||||
|
if stop == tsr.tokens.Size()-1 {
|
||||||
|
// Scan any remaining operations after last token
|
||||||
|
// should be included (they will be inserts).
|
||||||
|
for _, op := range indexToOp {
|
||||||
|
if op.GetIndex() >= tsr.tokens.Size()-1 {
|
||||||
|
buf.WriteString(op.GetText())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to combine operations and report invalid operations (like
|
||||||
|
// overlapping replaces that are not completed nested). Inserts to
|
||||||
|
// same index need to be combined etc... Here are the cases:
|
||||||
|
//
|
||||||
|
// I.i.u I.j.v leave alone, nonoverlapping
|
||||||
|
// I.i.u I.i.v combine: Iivu
|
||||||
|
//
|
||||||
|
// R.i-j.u R.x-y.v | i-j in x-y delete first R
|
||||||
|
// R.i-j.u R.i-j.v delete first R
|
||||||
|
// R.i-j.u R.x-y.v | x-y in i-j ERROR
|
||||||
|
// R.i-j.u R.x-y.v | boundaries overlap ERROR
|
||||||
|
//
|
||||||
|
// Delete special case of replace (text==null):
|
||||||
|
// D.i-j.u D.x-y.v | boundaries overlap combine to max(min)..max(right)
|
||||||
|
//
|
||||||
|
// I.i.u R.x-y.v | i in (x+1)-y delete I (since insert before
|
||||||
|
// we're not deleting i)
|
||||||
|
// I.i.u R.x-y.v | i not in (x+1)-y leave alone, nonoverlapping
|
||||||
|
// R.x-y.v I.i.u | i in x-y ERROR
|
||||||
|
// R.x-y.v I.x.u R.x-y.uv (combine, delete I)
|
||||||
|
// R.x-y.v I.i.u | i not in x-y leave alone, nonoverlapping
|
||||||
|
//
|
||||||
|
// I.i.u = insert u before op @ index i
|
||||||
|
// R.x-y.u = replace x-y indexed tokens with u
|
||||||
|
//
|
||||||
|
// First we need to examine replaces. For any replace op:
|
||||||
|
//
|
||||||
|
// 1. wipe out any insertions before op within that range.
|
||||||
|
// 2. Drop any replace op before that is contained completely within
|
||||||
|
// that range.
|
||||||
|
// 3. Throw exception upon boundary overlap with any previous replace.
|
||||||
|
//
|
||||||
|
// Then we can deal with inserts:
|
||||||
|
//
|
||||||
|
// 1. for any inserts to same index, combine even if not adjacent.
|
||||||
|
// 2. for any prior replace with same left boundary, combine this
|
||||||
|
// insert with replace and delete this replace.
|
||||||
|
// 3. throw exception if index in same range as previous replace
|
||||||
|
//
|
||||||
|
// Don't actually delete; make op null in list. Easier to walk list.
|
||||||
|
// Later we can throw as we add to index → op map.
|
||||||
|
//
|
||||||
|
// Note that I.2 R.2-2 will wipe out I.2 even though, technically, the
|
||||||
|
// inserted stuff would be before the replace range. But, if you
|
||||||
|
// add tokens in front of a method body '{' and then delete the method
|
||||||
|
// body, I think the stuff before the '{' you added should disappear too.
|
||||||
|
//
|
||||||
|
// Return a map from token index to operation.
|
||||||
|
func reduceToSingleOperationPerIndex(rewrites []RewriteOperation) map[int]RewriteOperation {
|
||||||
|
// WALK REPLACES
|
||||||
|
for i := 0; i < len(rewrites); i++ {
|
||||||
|
op := rewrites[i]
|
||||||
|
if op == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
rop, ok := op.(*ReplaceOp)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Wipe prior inserts within range
|
||||||
|
for j := 0; j < i && j < len(rewrites); j++ {
|
||||||
|
if iop, ok := rewrites[j].(*InsertBeforeOp); ok {
|
||||||
|
if iop.index == rop.index {
|
||||||
|
// E.g., insert before 2, delete 2..2; update replace
|
||||||
|
// text to include insert before, kill insert
|
||||||
|
rewrites[iop.instruction_index] = nil
|
||||||
|
if rop.text != "" {
|
||||||
|
rop.text = iop.text + rop.text
|
||||||
|
} else {
|
||||||
|
rop.text = iop.text
|
||||||
|
}
|
||||||
|
} else if iop.index > rop.index && iop.index <= rop.LastIndex {
|
||||||
|
// delete insert as it's a no-op.
|
||||||
|
rewrites[iop.instruction_index] = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Drop any prior replaces contained within
|
||||||
|
for j := 0; j < i && j < len(rewrites); j++ {
|
||||||
|
if prevop, ok := rewrites[j].(*ReplaceOp); ok {
|
||||||
|
if prevop.index >= rop.index && prevop.LastIndex <= rop.LastIndex {
|
||||||
|
// delete replace as it's a no-op.
|
||||||
|
rewrites[prevop.instruction_index] = nil
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// throw exception unless disjoint or identical
|
||||||
|
disjoint := prevop.LastIndex < rop.index || prevop.index > rop.LastIndex
|
||||||
|
// Delete special case of replace (text==null):
|
||||||
|
// D.i-j.u D.x-y.v | boundaries overlap combine to max(min)..max(right)
|
||||||
|
if prevop.text == "" && rop.text == "" && !disjoint {
|
||||||
|
rewrites[prevop.instruction_index] = nil
|
||||||
|
rop.index = min(prevop.index, rop.index)
|
||||||
|
rop.LastIndex = max(prevop.LastIndex, rop.LastIndex)
|
||||||
|
println("new rop" + rop.String()) //TODO: remove console write, taken from Java version
|
||||||
|
} else if !disjoint {
|
||||||
|
panic("replace op boundaries of " + rop.String() + " overlap with previous " + prevop.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// WALK INSERTS
|
||||||
|
for i := 0; i < len(rewrites); i++ {
|
||||||
|
op := rewrites[i]
|
||||||
|
if op == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
//hack to replicate inheritance in composition
|
||||||
|
_, iok := rewrites[i].(*InsertBeforeOp)
|
||||||
|
_, aok := rewrites[i].(*InsertAfterOp)
|
||||||
|
if !iok && !aok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
iop := rewrites[i]
|
||||||
|
// combine current insert with prior if any at same index
|
||||||
|
// deviating a bit from TokenStreamRewriter.java - hard to incorporate inheritance logic
|
||||||
|
for j := 0; j < i && j < len(rewrites); j++ {
|
||||||
|
if nextIop, ok := rewrites[j].(*InsertAfterOp); ok {
|
||||||
|
if nextIop.index == iop.GetIndex() {
|
||||||
|
iop.SetText(nextIop.text + iop.GetText())
|
||||||
|
rewrites[j] = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if prevIop, ok := rewrites[j].(*InsertBeforeOp); ok {
|
||||||
|
if prevIop.index == iop.GetIndex() {
|
||||||
|
iop.SetText(iop.GetText() + prevIop.text)
|
||||||
|
rewrites[prevIop.instruction_index] = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// look for replaces where iop.index is in range; error
|
||||||
|
for j := 0; j < i && j < len(rewrites); j++ {
|
||||||
|
if rop, ok := rewrites[j].(*ReplaceOp); ok {
|
||||||
|
if iop.GetIndex() == rop.index {
|
||||||
|
rop.text = iop.GetText() + rop.text
|
||||||
|
rewrites[i] = nil
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if iop.GetIndex() >= rop.index && iop.GetIndex() <= rop.LastIndex {
|
||||||
|
panic("insert op " + iop.String() + " within boundaries of previous " + rop.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m := map[int]RewriteOperation{}
|
||||||
|
for i := 0; i < len(rewrites); i++ {
|
||||||
|
op := rewrites[i]
|
||||||
|
if op == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, ok := m[op.GetIndex()]; ok {
|
||||||
|
panic("should only be one op per index")
|
||||||
|
}
|
||||||
|
m[op.GetIndex()] = op
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Quick fixing Go lack of overloads
|
||||||
|
*/
|
||||||
|
|
||||||
|
func max(a, b int) int {
|
||||||
|
if a > b {
|
||||||
|
return a
|
||||||
|
} else {
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func min(a, b int) int {
|
||||||
|
if a < b {
|
||||||
|
return a
|
||||||
|
} else {
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
}
|
||||||
32
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/trace_listener.go
generated
vendored
Normal file
32
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/trace_listener.go
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
||||||
|
// Use of this file is governed by the BSD 3-clause license that
|
||||||
|
// can be found in the LICENSE.txt file in the project root.
|
||||||
|
|
||||||
|
package antlr
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type TraceListener struct {
|
||||||
|
parser *BaseParser
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTraceListener(parser *BaseParser) *TraceListener {
|
||||||
|
tl := new(TraceListener)
|
||||||
|
tl.parser = parser
|
||||||
|
return tl
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TraceListener) VisitErrorNode(_ ErrorNode) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TraceListener) EnterEveryRule(ctx ParserRuleContext) {
|
||||||
|
fmt.Println("enter " + t.parser.GetRuleNames()[ctx.GetRuleIndex()] + ", LT(1)=" + t.parser.input.LT(1).GetText())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TraceListener) VisitTerminal(node TerminalNode) {
|
||||||
|
fmt.Println("consume " + fmt.Sprint(node.GetSymbol()) + " rule " + t.parser.GetRuleNames()[t.parser.ctx.GetRuleIndex()])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TraceListener) ExitEveryRule(ctx ParserRuleContext) {
|
||||||
|
fmt.Println("exit " + t.parser.GetRuleNames()[ctx.GetRuleIndex()] + ", LT(1)=" + t.parser.input.LT(1).GetText())
|
||||||
|
}
|
||||||
428
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/transition.go
generated
vendored
Normal file
428
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/transition.go
generated
vendored
Normal file
@@ -0,0 +1,428 @@
|
|||||||
|
// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
||||||
|
// Use of this file is governed by the BSD 3-clause license that
|
||||||
|
// can be found in the LICENSE.txt file in the project root.
|
||||||
|
|
||||||
|
package antlr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// atom, set, epsilon, action, predicate, rule transitions.
|
||||||
|
//
|
||||||
|
// <p>This is a one way link. It emanates from a state (usually via a list of
|
||||||
|
// transitions) and has a target state.</p>
|
||||||
|
//
|
||||||
|
// <p>Since we never have to change the ATN transitions once we construct it,
|
||||||
|
// the states. We'll use the term Edge for the DFA to distinguish them from
|
||||||
|
// ATN transitions.</p>
|
||||||
|
|
||||||
|
type Transition interface {
|
||||||
|
getTarget() ATNState
|
||||||
|
setTarget(ATNState)
|
||||||
|
getIsEpsilon() bool
|
||||||
|
getLabel() *IntervalSet
|
||||||
|
getSerializationType() int
|
||||||
|
Matches(int, int, int) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type BaseTransition struct {
|
||||||
|
target ATNState
|
||||||
|
isEpsilon bool
|
||||||
|
label int
|
||||||
|
intervalSet *IntervalSet
|
||||||
|
serializationType int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBaseTransition(target ATNState) *BaseTransition {
|
||||||
|
|
||||||
|
if target == nil {
|
||||||
|
panic("target cannot be nil.")
|
||||||
|
}
|
||||||
|
|
||||||
|
t := new(BaseTransition)
|
||||||
|
|
||||||
|
t.target = target
|
||||||
|
// Are we epsilon, action, sempred?
|
||||||
|
t.isEpsilon = false
|
||||||
|
t.intervalSet = nil
|
||||||
|
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *BaseTransition) getTarget() ATNState {
|
||||||
|
return t.target
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *BaseTransition) setTarget(s ATNState) {
|
||||||
|
t.target = s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *BaseTransition) getIsEpsilon() bool {
|
||||||
|
return t.isEpsilon
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *BaseTransition) getLabel() *IntervalSet {
|
||||||
|
return t.intervalSet
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *BaseTransition) getSerializationType() int {
|
||||||
|
return t.serializationType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *BaseTransition) Matches(symbol, minVocabSymbol, maxVocabSymbol int) bool {
|
||||||
|
panic("Not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
TransitionEPSILON = 1
|
||||||
|
TransitionRANGE = 2
|
||||||
|
TransitionRULE = 3
|
||||||
|
TransitionPREDICATE = 4 // e.g., {isType(input.LT(1))}?
|
||||||
|
TransitionATOM = 5
|
||||||
|
TransitionACTION = 6
|
||||||
|
TransitionSET = 7 // ~(A|B) or ~atom, wildcard, which convert to next 2
|
||||||
|
TransitionNOTSET = 8
|
||||||
|
TransitionWILDCARD = 9
|
||||||
|
TransitionPRECEDENCE = 10
|
||||||
|
)
|
||||||
|
|
||||||
|
var TransitionserializationNames = []string{
|
||||||
|
"INVALID",
|
||||||
|
"EPSILON",
|
||||||
|
"RANGE",
|
||||||
|
"RULE",
|
||||||
|
"PREDICATE",
|
||||||
|
"ATOM",
|
||||||
|
"ACTION",
|
||||||
|
"SET",
|
||||||
|
"NOT_SET",
|
||||||
|
"WILDCARD",
|
||||||
|
"PRECEDENCE",
|
||||||
|
}
|
||||||
|
|
||||||
|
//var TransitionserializationTypes struct {
|
||||||
|
// EpsilonTransition int
|
||||||
|
// RangeTransition int
|
||||||
|
// RuleTransition int
|
||||||
|
// PredicateTransition int
|
||||||
|
// AtomTransition int
|
||||||
|
// ActionTransition int
|
||||||
|
// SetTransition int
|
||||||
|
// NotSetTransition int
|
||||||
|
// WildcardTransition int
|
||||||
|
// PrecedencePredicateTransition int
|
||||||
|
//}{
|
||||||
|
// TransitionEPSILON,
|
||||||
|
// TransitionRANGE,
|
||||||
|
// TransitionRULE,
|
||||||
|
// TransitionPREDICATE,
|
||||||
|
// TransitionATOM,
|
||||||
|
// TransitionACTION,
|
||||||
|
// TransitionSET,
|
||||||
|
// TransitionNOTSET,
|
||||||
|
// TransitionWILDCARD,
|
||||||
|
// TransitionPRECEDENCE
|
||||||
|
//}
|
||||||
|
|
||||||
|
// TODO: make all transitions sets? no, should remove set edges
|
||||||
|
type AtomTransition struct {
|
||||||
|
*BaseTransition
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAtomTransition(target ATNState, intervalSet int) *AtomTransition {
|
||||||
|
|
||||||
|
t := new(AtomTransition)
|
||||||
|
t.BaseTransition = NewBaseTransition(target)
|
||||||
|
|
||||||
|
t.label = intervalSet // The token type or character value or, signifies special intervalSet.
|
||||||
|
t.intervalSet = t.makeLabel()
|
||||||
|
t.serializationType = TransitionATOM
|
||||||
|
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *AtomTransition) makeLabel() *IntervalSet {
|
||||||
|
s := NewIntervalSet()
|
||||||
|
s.addOne(t.label)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *AtomTransition) Matches(symbol, minVocabSymbol, maxVocabSymbol int) bool {
|
||||||
|
return t.label == symbol
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *AtomTransition) String() string {
|
||||||
|
return strconv.Itoa(t.label)
|
||||||
|
}
|
||||||
|
|
||||||
|
type RuleTransition struct {
|
||||||
|
*BaseTransition
|
||||||
|
|
||||||
|
followState ATNState
|
||||||
|
ruleIndex, precedence int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRuleTransition(ruleStart ATNState, ruleIndex, precedence int, followState ATNState) *RuleTransition {
|
||||||
|
|
||||||
|
t := new(RuleTransition)
|
||||||
|
t.BaseTransition = NewBaseTransition(ruleStart)
|
||||||
|
|
||||||
|
t.ruleIndex = ruleIndex
|
||||||
|
t.precedence = precedence
|
||||||
|
t.followState = followState
|
||||||
|
t.serializationType = TransitionRULE
|
||||||
|
t.isEpsilon = true
|
||||||
|
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *RuleTransition) Matches(symbol, minVocabSymbol, maxVocabSymbol int) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
type EpsilonTransition struct {
|
||||||
|
*BaseTransition
|
||||||
|
|
||||||
|
outermostPrecedenceReturn int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewEpsilonTransition(target ATNState, outermostPrecedenceReturn int) *EpsilonTransition {
|
||||||
|
|
||||||
|
t := new(EpsilonTransition)
|
||||||
|
t.BaseTransition = NewBaseTransition(target)
|
||||||
|
|
||||||
|
t.serializationType = TransitionEPSILON
|
||||||
|
t.isEpsilon = true
|
||||||
|
t.outermostPrecedenceReturn = outermostPrecedenceReturn
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *EpsilonTransition) Matches(symbol, minVocabSymbol, maxVocabSymbol int) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *EpsilonTransition) String() string {
|
||||||
|
return "epsilon"
|
||||||
|
}
|
||||||
|
|
||||||
|
type RangeTransition struct {
|
||||||
|
*BaseTransition
|
||||||
|
|
||||||
|
start, stop int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRangeTransition(target ATNState, start, stop int) *RangeTransition {
|
||||||
|
|
||||||
|
t := new(RangeTransition)
|
||||||
|
t.BaseTransition = NewBaseTransition(target)
|
||||||
|
|
||||||
|
t.serializationType = TransitionRANGE
|
||||||
|
t.start = start
|
||||||
|
t.stop = stop
|
||||||
|
t.intervalSet = t.makeLabel()
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *RangeTransition) makeLabel() *IntervalSet {
|
||||||
|
s := NewIntervalSet()
|
||||||
|
s.addRange(t.start, t.stop)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *RangeTransition) Matches(symbol, minVocabSymbol, maxVocabSymbol int) bool {
|
||||||
|
return symbol >= t.start && symbol <= t.stop
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *RangeTransition) String() string {
|
||||||
|
var sb strings.Builder
|
||||||
|
sb.WriteByte('\'')
|
||||||
|
sb.WriteRune(rune(t.start))
|
||||||
|
sb.WriteString("'..'")
|
||||||
|
sb.WriteRune(rune(t.stop))
|
||||||
|
sb.WriteByte('\'')
|
||||||
|
return sb.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
type AbstractPredicateTransition interface {
|
||||||
|
Transition
|
||||||
|
IAbstractPredicateTransitionFoo()
|
||||||
|
}
|
||||||
|
|
||||||
|
type BaseAbstractPredicateTransition struct {
|
||||||
|
*BaseTransition
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBasePredicateTransition(target ATNState) *BaseAbstractPredicateTransition {
|
||||||
|
|
||||||
|
t := new(BaseAbstractPredicateTransition)
|
||||||
|
t.BaseTransition = NewBaseTransition(target)
|
||||||
|
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *BaseAbstractPredicateTransition) IAbstractPredicateTransitionFoo() {}
|
||||||
|
|
||||||
|
type PredicateTransition struct {
|
||||||
|
*BaseAbstractPredicateTransition
|
||||||
|
|
||||||
|
isCtxDependent bool
|
||||||
|
ruleIndex, predIndex int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPredicateTransition(target ATNState, ruleIndex, predIndex int, isCtxDependent bool) *PredicateTransition {
|
||||||
|
|
||||||
|
t := new(PredicateTransition)
|
||||||
|
t.BaseAbstractPredicateTransition = NewBasePredicateTransition(target)
|
||||||
|
|
||||||
|
t.serializationType = TransitionPREDICATE
|
||||||
|
t.ruleIndex = ruleIndex
|
||||||
|
t.predIndex = predIndex
|
||||||
|
t.isCtxDependent = isCtxDependent // e.g., $i ref in pred
|
||||||
|
t.isEpsilon = true
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *PredicateTransition) Matches(symbol, minVocabSymbol, maxVocabSymbol int) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *PredicateTransition) getPredicate() *Predicate {
|
||||||
|
return NewPredicate(t.ruleIndex, t.predIndex, t.isCtxDependent)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *PredicateTransition) String() string {
|
||||||
|
return "pred_" + strconv.Itoa(t.ruleIndex) + ":" + strconv.Itoa(t.predIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ActionTransition struct {
|
||||||
|
*BaseTransition
|
||||||
|
|
||||||
|
isCtxDependent bool
|
||||||
|
ruleIndex, actionIndex, predIndex int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewActionTransition(target ATNState, ruleIndex, actionIndex int, isCtxDependent bool) *ActionTransition {
|
||||||
|
|
||||||
|
t := new(ActionTransition)
|
||||||
|
t.BaseTransition = NewBaseTransition(target)
|
||||||
|
|
||||||
|
t.serializationType = TransitionACTION
|
||||||
|
t.ruleIndex = ruleIndex
|
||||||
|
t.actionIndex = actionIndex
|
||||||
|
t.isCtxDependent = isCtxDependent // e.g., $i ref in pred
|
||||||
|
t.isEpsilon = true
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *ActionTransition) Matches(symbol, minVocabSymbol, maxVocabSymbol int) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *ActionTransition) String() string {
|
||||||
|
return "action_" + strconv.Itoa(t.ruleIndex) + ":" + strconv.Itoa(t.actionIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
type SetTransition struct {
|
||||||
|
*BaseTransition
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSetTransition(target ATNState, set *IntervalSet) *SetTransition {
|
||||||
|
|
||||||
|
t := new(SetTransition)
|
||||||
|
t.BaseTransition = NewBaseTransition(target)
|
||||||
|
|
||||||
|
t.serializationType = TransitionSET
|
||||||
|
if set != nil {
|
||||||
|
t.intervalSet = set
|
||||||
|
} else {
|
||||||
|
t.intervalSet = NewIntervalSet()
|
||||||
|
t.intervalSet.addOne(TokenInvalidType)
|
||||||
|
}
|
||||||
|
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *SetTransition) Matches(symbol, minVocabSymbol, maxVocabSymbol int) bool {
|
||||||
|
return t.intervalSet.contains(symbol)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *SetTransition) String() string {
|
||||||
|
return t.intervalSet.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
type NotSetTransition struct {
|
||||||
|
*SetTransition
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNotSetTransition(target ATNState, set *IntervalSet) *NotSetTransition {
|
||||||
|
|
||||||
|
t := new(NotSetTransition)
|
||||||
|
|
||||||
|
t.SetTransition = NewSetTransition(target, set)
|
||||||
|
|
||||||
|
t.serializationType = TransitionNOTSET
|
||||||
|
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *NotSetTransition) Matches(symbol, minVocabSymbol, maxVocabSymbol int) bool {
|
||||||
|
return symbol >= minVocabSymbol && symbol <= maxVocabSymbol && !t.intervalSet.contains(symbol)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *NotSetTransition) String() string {
|
||||||
|
return "~" + t.intervalSet.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
type WildcardTransition struct {
|
||||||
|
*BaseTransition
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWildcardTransition(target ATNState) *WildcardTransition {
|
||||||
|
|
||||||
|
t := new(WildcardTransition)
|
||||||
|
t.BaseTransition = NewBaseTransition(target)
|
||||||
|
|
||||||
|
t.serializationType = TransitionWILDCARD
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *WildcardTransition) Matches(symbol, minVocabSymbol, maxVocabSymbol int) bool {
|
||||||
|
return symbol >= minVocabSymbol && symbol <= maxVocabSymbol
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *WildcardTransition) String() string {
|
||||||
|
return "."
|
||||||
|
}
|
||||||
|
|
||||||
|
type PrecedencePredicateTransition struct {
|
||||||
|
*BaseAbstractPredicateTransition
|
||||||
|
|
||||||
|
precedence int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPrecedencePredicateTransition(target ATNState, precedence int) *PrecedencePredicateTransition {
|
||||||
|
|
||||||
|
t := new(PrecedencePredicateTransition)
|
||||||
|
t.BaseAbstractPredicateTransition = NewBasePredicateTransition(target)
|
||||||
|
|
||||||
|
t.serializationType = TransitionPRECEDENCE
|
||||||
|
t.precedence = precedence
|
||||||
|
t.isEpsilon = true
|
||||||
|
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *PrecedencePredicateTransition) Matches(symbol, minVocabSymbol, maxVocabSymbol int) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *PrecedencePredicateTransition) getPredicate() *PrecedencePredicate {
|
||||||
|
return NewPrecedencePredicate(t.precedence)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *PrecedencePredicateTransition) String() string {
|
||||||
|
return fmt.Sprint(t.precedence) + " >= _p"
|
||||||
|
}
|
||||||
253
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/tree.go
generated
vendored
Normal file
253
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/tree.go
generated
vendored
Normal file
@@ -0,0 +1,253 @@
|
|||||||
|
// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
||||||
|
// Use of this file is governed by the BSD 3-clause license that
|
||||||
|
// can be found in the LICENSE.txt file in the project root.
|
||||||
|
|
||||||
|
package antlr
|
||||||
|
|
||||||
|
// The basic notion of a tree has a parent, a payload, and a list of children.
|
||||||
|
// It is the most abstract interface for all the trees used by ANTLR.
|
||||||
|
///
|
||||||
|
|
||||||
|
var TreeInvalidInterval = NewInterval(-1, -2)
|
||||||
|
|
||||||
|
type Tree interface {
|
||||||
|
GetParent() Tree
|
||||||
|
SetParent(Tree)
|
||||||
|
GetPayload() interface{}
|
||||||
|
GetChild(i int) Tree
|
||||||
|
GetChildCount() int
|
||||||
|
GetChildren() []Tree
|
||||||
|
}
|
||||||
|
|
||||||
|
type SyntaxTree interface {
|
||||||
|
Tree
|
||||||
|
|
||||||
|
GetSourceInterval() *Interval
|
||||||
|
}
|
||||||
|
|
||||||
|
type ParseTree interface {
|
||||||
|
SyntaxTree
|
||||||
|
|
||||||
|
Accept(Visitor ParseTreeVisitor) interface{}
|
||||||
|
GetText() string
|
||||||
|
|
||||||
|
ToStringTree([]string, Recognizer) string
|
||||||
|
}
|
||||||
|
|
||||||
|
type RuleNode interface {
|
||||||
|
ParseTree
|
||||||
|
|
||||||
|
GetRuleContext() RuleContext
|
||||||
|
GetBaseRuleContext() *BaseRuleContext
|
||||||
|
}
|
||||||
|
|
||||||
|
type TerminalNode interface {
|
||||||
|
ParseTree
|
||||||
|
|
||||||
|
GetSymbol() Token
|
||||||
|
}
|
||||||
|
|
||||||
|
type ErrorNode interface {
|
||||||
|
TerminalNode
|
||||||
|
|
||||||
|
errorNode()
|
||||||
|
}
|
||||||
|
|
||||||
|
type ParseTreeVisitor interface {
|
||||||
|
Visit(tree ParseTree) interface{}
|
||||||
|
VisitChildren(node RuleNode) interface{}
|
||||||
|
VisitTerminal(node TerminalNode) interface{}
|
||||||
|
VisitErrorNode(node ErrorNode) interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type BaseParseTreeVisitor struct{}
|
||||||
|
|
||||||
|
var _ ParseTreeVisitor = &BaseParseTreeVisitor{}
|
||||||
|
|
||||||
|
func (v *BaseParseTreeVisitor) Visit(tree ParseTree) interface{} { return tree.Accept(v) }
|
||||||
|
func (v *BaseParseTreeVisitor) VisitChildren(node RuleNode) interface{} { return nil }
|
||||||
|
func (v *BaseParseTreeVisitor) VisitTerminal(node TerminalNode) interface{} { return nil }
|
||||||
|
func (v *BaseParseTreeVisitor) VisitErrorNode(node ErrorNode) interface{} { return nil }
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
//func (this ParseTreeVisitor) Visit(ctx) {
|
||||||
|
// if (Utils.isArray(ctx)) {
|
||||||
|
// self := this
|
||||||
|
// return ctx.map(function(child) { return VisitAtom(self, child)})
|
||||||
|
// } else {
|
||||||
|
// return VisitAtom(this, ctx)
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//func VisitAtom(Visitor, ctx) {
|
||||||
|
// if (ctx.parser == nil) { //is terminal
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// name := ctx.parser.ruleNames[ctx.ruleIndex]
|
||||||
|
// funcName := "Visit" + Utils.titleCase(name)
|
||||||
|
//
|
||||||
|
// return Visitor[funcName](ctx)
|
||||||
|
//}
|
||||||
|
|
||||||
|
type ParseTreeListener interface {
|
||||||
|
VisitTerminal(node TerminalNode)
|
||||||
|
VisitErrorNode(node ErrorNode)
|
||||||
|
EnterEveryRule(ctx ParserRuleContext)
|
||||||
|
ExitEveryRule(ctx ParserRuleContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
type BaseParseTreeListener struct{}
|
||||||
|
|
||||||
|
var _ ParseTreeListener = &BaseParseTreeListener{}
|
||||||
|
|
||||||
|
func (l *BaseParseTreeListener) VisitTerminal(node TerminalNode) {}
|
||||||
|
func (l *BaseParseTreeListener) VisitErrorNode(node ErrorNode) {}
|
||||||
|
func (l *BaseParseTreeListener) EnterEveryRule(ctx ParserRuleContext) {}
|
||||||
|
func (l *BaseParseTreeListener) ExitEveryRule(ctx ParserRuleContext) {}
|
||||||
|
|
||||||
|
type TerminalNodeImpl struct {
|
||||||
|
parentCtx RuleContext
|
||||||
|
|
||||||
|
symbol Token
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ TerminalNode = &TerminalNodeImpl{}
|
||||||
|
|
||||||
|
func NewTerminalNodeImpl(symbol Token) *TerminalNodeImpl {
|
||||||
|
tn := new(TerminalNodeImpl)
|
||||||
|
|
||||||
|
tn.parentCtx = nil
|
||||||
|
tn.symbol = symbol
|
||||||
|
|
||||||
|
return tn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TerminalNodeImpl) GetChild(i int) Tree {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TerminalNodeImpl) GetChildren() []Tree {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TerminalNodeImpl) SetChildren(tree []Tree) {
|
||||||
|
panic("Cannot set children on terminal node")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TerminalNodeImpl) GetSymbol() Token {
|
||||||
|
return t.symbol
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TerminalNodeImpl) GetParent() Tree {
|
||||||
|
return t.parentCtx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TerminalNodeImpl) SetParent(tree Tree) {
|
||||||
|
t.parentCtx = tree.(RuleContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TerminalNodeImpl) GetPayload() interface{} {
|
||||||
|
return t.symbol
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TerminalNodeImpl) GetSourceInterval() *Interval {
|
||||||
|
if t.symbol == nil {
|
||||||
|
return TreeInvalidInterval
|
||||||
|
}
|
||||||
|
tokenIndex := t.symbol.GetTokenIndex()
|
||||||
|
return NewInterval(tokenIndex, tokenIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TerminalNodeImpl) GetChildCount() int {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TerminalNodeImpl) Accept(v ParseTreeVisitor) interface{} {
|
||||||
|
return v.VisitTerminal(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TerminalNodeImpl) GetText() string {
|
||||||
|
return t.symbol.GetText()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TerminalNodeImpl) String() string {
|
||||||
|
if t.symbol.GetTokenType() == TokenEOF {
|
||||||
|
return "<EOF>"
|
||||||
|
}
|
||||||
|
|
||||||
|
return t.symbol.GetText()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TerminalNodeImpl) ToStringTree(s []string, r Recognizer) string {
|
||||||
|
return t.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Represents a token that was consumed during reSynchronization
|
||||||
|
// rather than during a valid Match operation. For example,
|
||||||
|
// we will create this kind of a node during single token insertion
|
||||||
|
// and deletion as well as during "consume until error recovery set"
|
||||||
|
// upon no viable alternative exceptions.
|
||||||
|
|
||||||
|
type ErrorNodeImpl struct {
|
||||||
|
*TerminalNodeImpl
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ ErrorNode = &ErrorNodeImpl{}
|
||||||
|
|
||||||
|
func NewErrorNodeImpl(token Token) *ErrorNodeImpl {
|
||||||
|
en := new(ErrorNodeImpl)
|
||||||
|
en.TerminalNodeImpl = NewTerminalNodeImpl(token)
|
||||||
|
return en
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *ErrorNodeImpl) errorNode() {}
|
||||||
|
|
||||||
|
func (e *ErrorNodeImpl) Accept(v ParseTreeVisitor) interface{} {
|
||||||
|
return v.VisitErrorNode(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ParseTreeWalker struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewParseTreeWalker() *ParseTreeWalker {
|
||||||
|
return new(ParseTreeWalker)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Performs a walk on the given parse tree starting at the root and going down recursively
|
||||||
|
// with depth-first search. On each node, EnterRule is called before
|
||||||
|
// recursively walking down into child nodes, then
|
||||||
|
// ExitRule is called after the recursive call to wind up.
|
||||||
|
func (p *ParseTreeWalker) Walk(listener ParseTreeListener, t Tree) {
|
||||||
|
switch tt := t.(type) {
|
||||||
|
case ErrorNode:
|
||||||
|
listener.VisitErrorNode(tt)
|
||||||
|
case TerminalNode:
|
||||||
|
listener.VisitTerminal(tt)
|
||||||
|
default:
|
||||||
|
p.EnterRule(listener, t.(RuleNode))
|
||||||
|
for i := 0; i < t.GetChildCount(); i++ {
|
||||||
|
child := t.GetChild(i)
|
||||||
|
p.Walk(listener, child)
|
||||||
|
}
|
||||||
|
p.ExitRule(listener, t.(RuleNode))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enters a grammar rule by first triggering the generic event {@link ParseTreeListener//EnterEveryRule}
|
||||||
|
// then by triggering the event specific to the given parse tree node
|
||||||
|
func (p *ParseTreeWalker) EnterRule(listener ParseTreeListener, r RuleNode) {
|
||||||
|
ctx := r.GetRuleContext().(ParserRuleContext)
|
||||||
|
listener.EnterEveryRule(ctx)
|
||||||
|
ctx.EnterRule(listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exits a grammar rule by first triggering the event specific to the given parse tree node
|
||||||
|
// then by triggering the generic event {@link ParseTreeListener//ExitEveryRule}
|
||||||
|
func (p *ParseTreeWalker) ExitRule(listener ParseTreeListener, r RuleNode) {
|
||||||
|
ctx := r.GetRuleContext().(ParserRuleContext)
|
||||||
|
ctx.ExitRule(listener)
|
||||||
|
listener.ExitEveryRule(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
var ParseTreeWalkerDefault = NewParseTreeWalker()
|
||||||
138
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/trees.go
generated
vendored
Normal file
138
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/trees.go
generated
vendored
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
||||||
|
// Use of this file is governed by the BSD 3-clause license that
|
||||||
|
// can be found in the LICENSE.txt file in the project root.
|
||||||
|
|
||||||
|
package antlr
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
/** A set of utility routines useful for all kinds of ANTLR trees. */
|
||||||
|
|
||||||
|
// Print out a whole tree in LISP form. {@link //getNodeText} is used on the
|
||||||
|
//
|
||||||
|
// node payloads to get the text for the nodes. Detect
|
||||||
|
// parse trees and extract data appropriately.
|
||||||
|
func TreesStringTree(tree Tree, ruleNames []string, recog Recognizer) string {
|
||||||
|
|
||||||
|
if recog != nil {
|
||||||
|
ruleNames = recog.GetRuleNames()
|
||||||
|
}
|
||||||
|
|
||||||
|
s := TreesGetNodeText(tree, ruleNames, nil)
|
||||||
|
|
||||||
|
s = EscapeWhitespace(s, false)
|
||||||
|
c := tree.GetChildCount()
|
||||||
|
if c == 0 {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
res := "(" + s + " "
|
||||||
|
if c > 0 {
|
||||||
|
s = TreesStringTree(tree.GetChild(0), ruleNames, nil)
|
||||||
|
res += s
|
||||||
|
}
|
||||||
|
for i := 1; i < c; i++ {
|
||||||
|
s = TreesStringTree(tree.GetChild(i), ruleNames, nil)
|
||||||
|
res += (" " + s)
|
||||||
|
}
|
||||||
|
res += ")"
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func TreesGetNodeText(t Tree, ruleNames []string, recog Parser) string {
|
||||||
|
if recog != nil {
|
||||||
|
ruleNames = recog.GetRuleNames()
|
||||||
|
}
|
||||||
|
|
||||||
|
if ruleNames != nil {
|
||||||
|
switch t2 := t.(type) {
|
||||||
|
case RuleNode:
|
||||||
|
t3 := t2.GetRuleContext()
|
||||||
|
altNumber := t3.GetAltNumber()
|
||||||
|
|
||||||
|
if altNumber != ATNInvalidAltNumber {
|
||||||
|
return fmt.Sprintf("%s:%d", ruleNames[t3.GetRuleIndex()], altNumber)
|
||||||
|
}
|
||||||
|
return ruleNames[t3.GetRuleIndex()]
|
||||||
|
case ErrorNode:
|
||||||
|
return fmt.Sprint(t2)
|
||||||
|
case TerminalNode:
|
||||||
|
if t2.GetSymbol() != nil {
|
||||||
|
return t2.GetSymbol().GetText()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// no recog for rule names
|
||||||
|
payload := t.GetPayload()
|
||||||
|
if p2, ok := payload.(Token); ok {
|
||||||
|
return p2.GetText()
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprint(t.GetPayload())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return ordered list of all children of this node
|
||||||
|
func TreesGetChildren(t Tree) []Tree {
|
||||||
|
list := make([]Tree, 0)
|
||||||
|
for i := 0; i < t.GetChildCount(); i++ {
|
||||||
|
list = append(list, t.GetChild(i))
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return a list of all ancestors of this node. The first node of
|
||||||
|
//
|
||||||
|
// list is the root and the last is the parent of this node.
|
||||||
|
func TreesgetAncestors(t Tree) []Tree {
|
||||||
|
ancestors := make([]Tree, 0)
|
||||||
|
t = t.GetParent()
|
||||||
|
for t != nil {
|
||||||
|
f := []Tree{t}
|
||||||
|
ancestors = append(f, ancestors...)
|
||||||
|
t = t.GetParent()
|
||||||
|
}
|
||||||
|
return ancestors
|
||||||
|
}
|
||||||
|
|
||||||
|
func TreesFindAllTokenNodes(t ParseTree, ttype int) []ParseTree {
|
||||||
|
return TreesfindAllNodes(t, ttype, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TreesfindAllRuleNodes(t ParseTree, ruleIndex int) []ParseTree {
|
||||||
|
return TreesfindAllNodes(t, ruleIndex, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TreesfindAllNodes(t ParseTree, index int, findTokens bool) []ParseTree {
|
||||||
|
nodes := make([]ParseTree, 0)
|
||||||
|
treesFindAllNodes(t, index, findTokens, &nodes)
|
||||||
|
return nodes
|
||||||
|
}
|
||||||
|
|
||||||
|
func treesFindAllNodes(t ParseTree, index int, findTokens bool, nodes *[]ParseTree) {
|
||||||
|
// check this node (the root) first
|
||||||
|
|
||||||
|
t2, ok := t.(TerminalNode)
|
||||||
|
t3, ok2 := t.(ParserRuleContext)
|
||||||
|
|
||||||
|
if findTokens && ok {
|
||||||
|
if t2.GetSymbol().GetTokenType() == index {
|
||||||
|
*nodes = append(*nodes, t2)
|
||||||
|
}
|
||||||
|
} else if !findTokens && ok2 {
|
||||||
|
if t3.GetRuleIndex() == index {
|
||||||
|
*nodes = append(*nodes, t3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// check children
|
||||||
|
for i := 0; i < t.GetChildCount(); i++ {
|
||||||
|
treesFindAllNodes(t.GetChild(i).(ParseTree), index, findTokens, nodes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TreesDescendants(t ParseTree) []ParseTree {
|
||||||
|
nodes := []ParseTree{t}
|
||||||
|
for i := 0; i < t.GetChildCount(); i++ {
|
||||||
|
nodes = append(nodes, TreesDescendants(t.GetChild(i).(ParseTree))...)
|
||||||
|
}
|
||||||
|
return nodes
|
||||||
|
}
|
||||||
352
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/utils.go
generated
vendored
Normal file
352
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/utils.go
generated
vendored
Normal file
@@ -0,0 +1,352 @@
|
|||||||
|
// Copyright (c) 2012-2022 The ANTLR Project. All rights reserved.
|
||||||
|
// Use of this file is governed by the BSD 3-clause license that
|
||||||
|
// can be found in the LICENSE.txt file in the project root.
|
||||||
|
|
||||||
|
package antlr
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math/bits"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func intMin(a, b int) int {
|
||||||
|
if a < b {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func intMax(a, b int) int {
|
||||||
|
if a > b {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// A simple integer stack
|
||||||
|
|
||||||
|
type IntStack []int
|
||||||
|
|
||||||
|
var ErrEmptyStack = errors.New("Stack is empty")
|
||||||
|
|
||||||
|
func (s *IntStack) Pop() (int, error) {
|
||||||
|
l := len(*s) - 1
|
||||||
|
if l < 0 {
|
||||||
|
return 0, ErrEmptyStack
|
||||||
|
}
|
||||||
|
v := (*s)[l]
|
||||||
|
*s = (*s)[0:l]
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *IntStack) Push(e int) {
|
||||||
|
*s = append(*s, e)
|
||||||
|
}
|
||||||
|
|
||||||
|
type comparable interface {
|
||||||
|
Equals(other Collectable[any]) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func standardEqualsFunction(a Collectable[any], b Collectable[any]) bool {
|
||||||
|
|
||||||
|
return a.Equals(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func standardHashFunction(a interface{}) int {
|
||||||
|
if h, ok := a.(hasher); ok {
|
||||||
|
return h.Hash()
|
||||||
|
}
|
||||||
|
|
||||||
|
panic("Not Hasher")
|
||||||
|
}
|
||||||
|
|
||||||
|
type hasher interface {
|
||||||
|
Hash() int
|
||||||
|
}
|
||||||
|
|
||||||
|
const bitsPerWord = 64
|
||||||
|
|
||||||
|
func indexForBit(bit int) int {
|
||||||
|
return bit / bitsPerWord
|
||||||
|
}
|
||||||
|
|
||||||
|
func wordForBit(data []uint64, bit int) uint64 {
|
||||||
|
idx := indexForBit(bit)
|
||||||
|
if idx >= len(data) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return data[idx]
|
||||||
|
}
|
||||||
|
|
||||||
|
func maskForBit(bit int) uint64 {
|
||||||
|
return uint64(1) << (bit % bitsPerWord)
|
||||||
|
}
|
||||||
|
|
||||||
|
func wordsNeeded(bit int) int {
|
||||||
|
return indexForBit(bit) + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
type BitSet struct {
|
||||||
|
data []uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBitSet() *BitSet {
|
||||||
|
return &BitSet{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BitSet) add(value int) {
|
||||||
|
idx := indexForBit(value)
|
||||||
|
if idx >= len(b.data) {
|
||||||
|
size := wordsNeeded(value)
|
||||||
|
data := make([]uint64, size)
|
||||||
|
copy(data, b.data)
|
||||||
|
b.data = data
|
||||||
|
}
|
||||||
|
b.data[idx] |= maskForBit(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BitSet) clear(index int) {
|
||||||
|
idx := indexForBit(index)
|
||||||
|
if idx >= len(b.data) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
b.data[idx] &= ^maskForBit(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BitSet) or(set *BitSet) {
|
||||||
|
// Get min size necessary to represent the bits in both sets.
|
||||||
|
bLen := b.minLen()
|
||||||
|
setLen := set.minLen()
|
||||||
|
maxLen := intMax(bLen, setLen)
|
||||||
|
if maxLen > len(b.data) {
|
||||||
|
// Increase the size of len(b.data) to repesent the bits in both sets.
|
||||||
|
data := make([]uint64, maxLen)
|
||||||
|
copy(data, b.data)
|
||||||
|
b.data = data
|
||||||
|
}
|
||||||
|
// len(b.data) is at least setLen.
|
||||||
|
for i := 0; i < setLen; i++ {
|
||||||
|
b.data[i] |= set.data[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BitSet) remove(value int) {
|
||||||
|
b.clear(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BitSet) contains(value int) bool {
|
||||||
|
idx := indexForBit(value)
|
||||||
|
if idx >= len(b.data) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return (b.data[idx] & maskForBit(value)) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BitSet) minValue() int {
|
||||||
|
for i, v := range b.data {
|
||||||
|
if v == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return i*bitsPerWord + bits.TrailingZeros64(v)
|
||||||
|
}
|
||||||
|
return 2147483647
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BitSet) equals(other interface{}) bool {
|
||||||
|
otherBitSet, ok := other.(*BitSet)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if b == otherBitSet {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// We only compare set bits, so we cannot rely on the two slices having the same size. Its
|
||||||
|
// possible for two BitSets to have different slice lengths but the same set bits. So we only
|
||||||
|
// compare the relevant words and ignore the trailing zeros.
|
||||||
|
bLen := b.minLen()
|
||||||
|
otherLen := otherBitSet.minLen()
|
||||||
|
|
||||||
|
if bLen != otherLen {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < bLen; i++ {
|
||||||
|
if b.data[i] != otherBitSet.data[i] {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BitSet) minLen() int {
|
||||||
|
for i := len(b.data); i > 0; i-- {
|
||||||
|
if b.data[i-1] != 0 {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BitSet) length() int {
|
||||||
|
cnt := 0
|
||||||
|
for _, val := range b.data {
|
||||||
|
cnt += bits.OnesCount64(val)
|
||||||
|
}
|
||||||
|
return cnt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BitSet) String() string {
|
||||||
|
vals := make([]string, 0, b.length())
|
||||||
|
|
||||||
|
for i, v := range b.data {
|
||||||
|
for v != 0 {
|
||||||
|
n := bits.TrailingZeros64(v)
|
||||||
|
vals = append(vals, strconv.Itoa(i*bitsPerWord+n))
|
||||||
|
v &= ^(uint64(1) << n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "{" + strings.Join(vals, ", ") + "}"
|
||||||
|
}
|
||||||
|
|
||||||
|
type AltDict struct {
|
||||||
|
data map[string]interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAltDict() *AltDict {
|
||||||
|
d := new(AltDict)
|
||||||
|
d.data = make(map[string]interface{})
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AltDict) Get(key string) interface{} {
|
||||||
|
key = "k-" + key
|
||||||
|
return a.data[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AltDict) put(key string, value interface{}) {
|
||||||
|
key = "k-" + key
|
||||||
|
a.data[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AltDict) values() []interface{} {
|
||||||
|
vs := make([]interface{}, len(a.data))
|
||||||
|
i := 0
|
||||||
|
for _, v := range a.data {
|
||||||
|
vs[i] = v
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return vs
|
||||||
|
}
|
||||||
|
|
||||||
|
type DoubleDict struct {
|
||||||
|
data map[int]map[int]interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDoubleDict() *DoubleDict {
|
||||||
|
dd := new(DoubleDict)
|
||||||
|
dd.data = make(map[int]map[int]interface{})
|
||||||
|
return dd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DoubleDict) Get(a, b int) interface{} {
|
||||||
|
data := d.data[a]
|
||||||
|
|
||||||
|
if data == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return data[b]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DoubleDict) set(a, b int, o interface{}) {
|
||||||
|
data := d.data[a]
|
||||||
|
|
||||||
|
if data == nil {
|
||||||
|
data = make(map[int]interface{})
|
||||||
|
d.data[a] = data
|
||||||
|
}
|
||||||
|
|
||||||
|
data[b] = o
|
||||||
|
}
|
||||||
|
|
||||||
|
func EscapeWhitespace(s string, escapeSpaces bool) string {
|
||||||
|
|
||||||
|
s = strings.Replace(s, "\t", "\\t", -1)
|
||||||
|
s = strings.Replace(s, "\n", "\\n", -1)
|
||||||
|
s = strings.Replace(s, "\r", "\\r", -1)
|
||||||
|
if escapeSpaces {
|
||||||
|
s = strings.Replace(s, " ", "\u00B7", -1)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func TerminalNodeToStringArray(sa []TerminalNode) []string {
|
||||||
|
st := make([]string, len(sa))
|
||||||
|
|
||||||
|
for i, s := range sa {
|
||||||
|
st[i] = fmt.Sprintf("%v", s)
|
||||||
|
}
|
||||||
|
|
||||||
|
return st
|
||||||
|
}
|
||||||
|
|
||||||
|
func PrintArrayJavaStyle(sa []string) string {
|
||||||
|
var buffer bytes.Buffer
|
||||||
|
|
||||||
|
buffer.WriteString("[")
|
||||||
|
|
||||||
|
for i, s := range sa {
|
||||||
|
buffer.WriteString(s)
|
||||||
|
if i != len(sa)-1 {
|
||||||
|
buffer.WriteString(", ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.WriteString("]")
|
||||||
|
|
||||||
|
return buffer.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// murmur hash
|
||||||
|
func murmurInit(seed int) int {
|
||||||
|
return seed
|
||||||
|
}
|
||||||
|
|
||||||
|
func murmurUpdate(h int, value int) int {
|
||||||
|
const c1 uint32 = 0xCC9E2D51
|
||||||
|
const c2 uint32 = 0x1B873593
|
||||||
|
const r1 uint32 = 15
|
||||||
|
const r2 uint32 = 13
|
||||||
|
const m uint32 = 5
|
||||||
|
const n uint32 = 0xE6546B64
|
||||||
|
|
||||||
|
k := uint32(value)
|
||||||
|
k *= c1
|
||||||
|
k = (k << r1) | (k >> (32 - r1))
|
||||||
|
k *= c2
|
||||||
|
|
||||||
|
hash := uint32(h) ^ k
|
||||||
|
hash = (hash << r2) | (hash >> (32 - r2))
|
||||||
|
hash = hash*m + n
|
||||||
|
return int(hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
func murmurFinish(h int, numberOfWords int) int {
|
||||||
|
var hash = uint32(h)
|
||||||
|
hash ^= uint32(numberOfWords) << 2
|
||||||
|
hash ^= hash >> 16
|
||||||
|
hash *= 0x85ebca6b
|
||||||
|
hash ^= hash >> 13
|
||||||
|
hash *= 0xc2b2ae35
|
||||||
|
hash ^= hash >> 16
|
||||||
|
|
||||||
|
return int(hash)
|
||||||
|
}
|
||||||
235
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/utils_set.go
generated
vendored
Normal file
235
vendor/github.com/antlr/antlr4/runtime/Go/antlr/v4/utils_set.go
generated
vendored
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
package antlr
|
||||||
|
|
||||||
|
import "math"
|
||||||
|
|
||||||
|
const (
|
||||||
|
_initalCapacity = 16
|
||||||
|
_initalBucketCapacity = 8
|
||||||
|
_loadFactor = 0.75
|
||||||
|
)
|
||||||
|
|
||||||
|
type Set interface {
|
||||||
|
Add(value interface{}) (added interface{})
|
||||||
|
Len() int
|
||||||
|
Get(value interface{}) (found interface{})
|
||||||
|
Contains(value interface{}) bool
|
||||||
|
Values() []interface{}
|
||||||
|
Each(f func(interface{}) bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
type array2DHashSet struct {
|
||||||
|
buckets [][]Collectable[any]
|
||||||
|
hashcodeFunction func(interface{}) int
|
||||||
|
equalsFunction func(Collectable[any], Collectable[any]) bool
|
||||||
|
|
||||||
|
n int // How many elements in set
|
||||||
|
threshold int // when to expand
|
||||||
|
|
||||||
|
currentPrime int // jump by 4 primes each expand or whatever
|
||||||
|
initialBucketCapacity int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as *array2DHashSet) Each(f func(interface{}) bool) {
|
||||||
|
if as.Len() < 1 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, bucket := range as.buckets {
|
||||||
|
for _, o := range bucket {
|
||||||
|
if o == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if !f(o) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as *array2DHashSet) Values() []interface{} {
|
||||||
|
if as.Len() < 1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
values := make([]interface{}, 0, as.Len())
|
||||||
|
as.Each(func(i interface{}) bool {
|
||||||
|
values = append(values, i)
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
return values
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as *array2DHashSet) Contains(value Collectable[any]) bool {
|
||||||
|
return as.Get(value) != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as *array2DHashSet) Add(value Collectable[any]) interface{} {
|
||||||
|
if as.n > as.threshold {
|
||||||
|
as.expand()
|
||||||
|
}
|
||||||
|
return as.innerAdd(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as *array2DHashSet) expand() {
|
||||||
|
old := as.buckets
|
||||||
|
|
||||||
|
as.currentPrime += 4
|
||||||
|
|
||||||
|
var (
|
||||||
|
newCapacity = len(as.buckets) << 1
|
||||||
|
newTable = as.createBuckets(newCapacity)
|
||||||
|
newBucketLengths = make([]int, len(newTable))
|
||||||
|
)
|
||||||
|
|
||||||
|
as.buckets = newTable
|
||||||
|
as.threshold = int(float64(newCapacity) * _loadFactor)
|
||||||
|
|
||||||
|
for _, bucket := range old {
|
||||||
|
if bucket == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, o := range bucket {
|
||||||
|
if o == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
b := as.getBuckets(o)
|
||||||
|
bucketLength := newBucketLengths[b]
|
||||||
|
var newBucket []Collectable[any]
|
||||||
|
if bucketLength == 0 {
|
||||||
|
// new bucket
|
||||||
|
newBucket = as.createBucket(as.initialBucketCapacity)
|
||||||
|
newTable[b] = newBucket
|
||||||
|
} else {
|
||||||
|
newBucket = newTable[b]
|
||||||
|
if bucketLength == len(newBucket) {
|
||||||
|
// expand
|
||||||
|
newBucketCopy := make([]Collectable[any], len(newBucket)<<1)
|
||||||
|
copy(newBucketCopy[:bucketLength], newBucket)
|
||||||
|
newBucket = newBucketCopy
|
||||||
|
newTable[b] = newBucket
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newBucket[bucketLength] = o
|
||||||
|
newBucketLengths[b]++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as *array2DHashSet) Len() int {
|
||||||
|
return as.n
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as *array2DHashSet) Get(o Collectable[any]) interface{} {
|
||||||
|
if o == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
b := as.getBuckets(o)
|
||||||
|
bucket := as.buckets[b]
|
||||||
|
if bucket == nil { // no bucket
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, e := range bucket {
|
||||||
|
if e == nil {
|
||||||
|
return nil // empty slot; not there
|
||||||
|
}
|
||||||
|
if as.equalsFunction(e, o) {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as *array2DHashSet) innerAdd(o Collectable[any]) interface{} {
|
||||||
|
b := as.getBuckets(o)
|
||||||
|
|
||||||
|
bucket := as.buckets[b]
|
||||||
|
|
||||||
|
// new bucket
|
||||||
|
if bucket == nil {
|
||||||
|
bucket = as.createBucket(as.initialBucketCapacity)
|
||||||
|
bucket[0] = o
|
||||||
|
|
||||||
|
as.buckets[b] = bucket
|
||||||
|
as.n++
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
// look for it in bucket
|
||||||
|
for i := 0; i < len(bucket); i++ {
|
||||||
|
existing := bucket[i]
|
||||||
|
if existing == nil { // empty slot; not there, add.
|
||||||
|
bucket[i] = o
|
||||||
|
as.n++
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
if as.equalsFunction(existing, o) { // found existing, quit
|
||||||
|
return existing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// full bucket, expand and add to end
|
||||||
|
oldLength := len(bucket)
|
||||||
|
bucketCopy := make([]Collectable[any], oldLength<<1)
|
||||||
|
copy(bucketCopy[:oldLength], bucket)
|
||||||
|
bucket = bucketCopy
|
||||||
|
as.buckets[b] = bucket
|
||||||
|
bucket[oldLength] = o
|
||||||
|
as.n++
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as *array2DHashSet) getBuckets(value Collectable[any]) int {
|
||||||
|
hash := as.hashcodeFunction(value)
|
||||||
|
return hash & (len(as.buckets) - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as *array2DHashSet) createBuckets(cap int) [][]Collectable[any] {
|
||||||
|
return make([][]Collectable[any], cap)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (as *array2DHashSet) createBucket(cap int) []Collectable[any] {
|
||||||
|
return make([]Collectable[any], cap)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newArray2DHashSetWithCap(
|
||||||
|
hashcodeFunction func(interface{}) int,
|
||||||
|
equalsFunction func(Collectable[any], Collectable[any]) bool,
|
||||||
|
initCap int,
|
||||||
|
initBucketCap int,
|
||||||
|
) *array2DHashSet {
|
||||||
|
if hashcodeFunction == nil {
|
||||||
|
hashcodeFunction = standardHashFunction
|
||||||
|
}
|
||||||
|
|
||||||
|
if equalsFunction == nil {
|
||||||
|
equalsFunction = standardEqualsFunction
|
||||||
|
}
|
||||||
|
|
||||||
|
ret := &array2DHashSet{
|
||||||
|
hashcodeFunction: hashcodeFunction,
|
||||||
|
equalsFunction: equalsFunction,
|
||||||
|
|
||||||
|
n: 0,
|
||||||
|
threshold: int(math.Floor(_initalCapacity * _loadFactor)),
|
||||||
|
|
||||||
|
currentPrime: 1,
|
||||||
|
initialBucketCapacity: initBucketCap,
|
||||||
|
}
|
||||||
|
|
||||||
|
ret.buckets = ret.createBuckets(initCap)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func newArray2DHashSet(
|
||||||
|
hashcodeFunction func(interface{}) int,
|
||||||
|
equalsFunction func(Collectable[any], Collectable[any]) bool,
|
||||||
|
) *array2DHashSet {
|
||||||
|
return newArray2DHashSetWithCap(hashcodeFunction, equalsFunction, _initalCapacity, _initalBucketCapacity)
|
||||||
|
}
|
||||||
9
vendor/github.com/go-sql-driver/mysql/.gitignore
generated
vendored
9
vendor/github.com/go-sql-driver/mysql/.gitignore
generated
vendored
@@ -1,9 +0,0 @@
|
|||||||
.DS_Store
|
|
||||||
.DS_Store?
|
|
||||||
._*
|
|
||||||
.Spotlight-V100
|
|
||||||
.Trashes
|
|
||||||
Icon?
|
|
||||||
ehthumbs.db
|
|
||||||
Thumbs.db
|
|
||||||
.idea
|
|
||||||
126
vendor/github.com/go-sql-driver/mysql/AUTHORS
generated
vendored
126
vendor/github.com/go-sql-driver/mysql/AUTHORS
generated
vendored
@@ -1,126 +0,0 @@
|
|||||||
# This is the official list of Go-MySQL-Driver authors for copyright purposes.
|
|
||||||
|
|
||||||
# If you are submitting a patch, please add your name or the name of the
|
|
||||||
# organization which holds the copyright to this list in alphabetical order.
|
|
||||||
|
|
||||||
# Names should be added to this file as
|
|
||||||
# Name <email address>
|
|
||||||
# The email address is not required for organizations.
|
|
||||||
# Please keep the list sorted.
|
|
||||||
|
|
||||||
|
|
||||||
# Individual Persons
|
|
||||||
|
|
||||||
Aaron Hopkins <go-sql-driver at die.net>
|
|
||||||
Achille Roussel <achille.roussel at gmail.com>
|
|
||||||
Alex Snast <alexsn at fb.com>
|
|
||||||
Alexey Palazhchenko <alexey.palazhchenko at gmail.com>
|
|
||||||
Andrew Reid <andrew.reid at tixtrack.com>
|
|
||||||
Animesh Ray <mail.rayanimesh at gmail.com>
|
|
||||||
Arne Hormann <arnehormann at gmail.com>
|
|
||||||
Ariel Mashraki <ariel at mashraki.co.il>
|
|
||||||
Asta Xie <xiemengjun at gmail.com>
|
|
||||||
Bulat Gaifullin <gaifullinbf at gmail.com>
|
|
||||||
Caine Jette <jette at alum.mit.edu>
|
|
||||||
Carlos Nieto <jose.carlos at menteslibres.net>
|
|
||||||
Chris Kirkland <chriskirkland at github.com>
|
|
||||||
Chris Moos <chris at tech9computers.com>
|
|
||||||
Craig Wilson <craiggwilson at gmail.com>
|
|
||||||
Daniel Montoya <dsmontoyam at gmail.com>
|
|
||||||
Daniel Nichter <nil at codenode.com>
|
|
||||||
Daniël van Eeden <git at myname.nl>
|
|
||||||
Dave Protasowski <dprotaso at gmail.com>
|
|
||||||
DisposaBoy <disposaboy at dby.me>
|
|
||||||
Egor Smolyakov <egorsmkv at gmail.com>
|
|
||||||
Erwan Martin <hello at erwan.io>
|
|
||||||
Evan Shaw <evan at vendhq.com>
|
|
||||||
Frederick Mayle <frederickmayle at gmail.com>
|
|
||||||
Gustavo Kristic <gkristic at gmail.com>
|
|
||||||
Hajime Nakagami <nakagami at gmail.com>
|
|
||||||
Hanno Braun <mail at hannobraun.com>
|
|
||||||
Henri Yandell <flamefew at gmail.com>
|
|
||||||
Hirotaka Yamamoto <ymmt2005 at gmail.com>
|
|
||||||
Huyiguang <hyg at webterren.com>
|
|
||||||
ICHINOSE Shogo <shogo82148 at gmail.com>
|
|
||||||
Ilia Cimpoes <ichimpoesh at gmail.com>
|
|
||||||
INADA Naoki <songofacandy at gmail.com>
|
|
||||||
Jacek Szwec <szwec.jacek at gmail.com>
|
|
||||||
James Harr <james.harr at gmail.com>
|
|
||||||
Janek Vedock <janekvedock at comcast.net>
|
|
||||||
Jeff Hodges <jeff at somethingsimilar.com>
|
|
||||||
Jeffrey Charles <jeffreycharles at gmail.com>
|
|
||||||
Jerome Meyer <jxmeyer at gmail.com>
|
|
||||||
Jiajia Zhong <zhong2plus at gmail.com>
|
|
||||||
Jian Zhen <zhenjl at gmail.com>
|
|
||||||
Joshua Prunier <joshua.prunier at gmail.com>
|
|
||||||
Julien Lefevre <julien.lefevr at gmail.com>
|
|
||||||
Julien Schmidt <go-sql-driver at julienschmidt.com>
|
|
||||||
Justin Li <jli at j-li.net>
|
|
||||||
Justin Nuß <nuss.justin at gmail.com>
|
|
||||||
Kamil Dziedzic <kamil at klecza.pl>
|
|
||||||
Kei Kamikawa <x00.x7f.x86 at gmail.com>
|
|
||||||
Kevin Malachowski <kevin at chowski.com>
|
|
||||||
Kieron Woodhouse <kieron.woodhouse at infosum.com>
|
|
||||||
Lance Tian <lance6716 at gmail.com>
|
|
||||||
Lennart Rudolph <lrudolph at hmc.edu>
|
|
||||||
Leonardo YongUk Kim <dalinaum at gmail.com>
|
|
||||||
Linh Tran Tuan <linhduonggnu at gmail.com>
|
|
||||||
Lion Yang <lion at aosc.xyz>
|
|
||||||
Luca Looz <luca.looz92 at gmail.com>
|
|
||||||
Lucas Liu <extrafliu at gmail.com>
|
|
||||||
Lunny Xiao <xiaolunwen at gmail.com>
|
|
||||||
Luke Scott <luke at webconnex.com>
|
|
||||||
Maciej Zimnoch <maciej.zimnoch at codilime.com>
|
|
||||||
Michael Woolnough <michael.woolnough at gmail.com>
|
|
||||||
Nathanial Murphy <nathanial.murphy at gmail.com>
|
|
||||||
Nicola Peduzzi <thenikso at gmail.com>
|
|
||||||
Olivier Mengué <dolmen at cpan.org>
|
|
||||||
oscarzhao <oscarzhaosl at gmail.com>
|
|
||||||
Paul Bonser <misterpib at gmail.com>
|
|
||||||
Peter Schultz <peter.schultz at classmarkets.com>
|
|
||||||
Phil Porada <philporada at gmail.com>
|
|
||||||
Rebecca Chin <rchin at pivotal.io>
|
|
||||||
Reed Allman <rdallman10 at gmail.com>
|
|
||||||
Richard Wilkes <wilkes at me.com>
|
|
||||||
Robert Russell <robert at rrbrussell.com>
|
|
||||||
Runrioter Wung <runrioter at gmail.com>
|
|
||||||
Samantha Frank <hello at entropy.cat>
|
|
||||||
Santhosh Kumar Tekuri <santhosh.tekuri at gmail.com>
|
|
||||||
Sho Iizuka <sho.i518 at gmail.com>
|
|
||||||
Sho Ikeda <suicaicoca at gmail.com>
|
|
||||||
Shuode Li <elemount at qq.com>
|
|
||||||
Simon J Mudd <sjmudd at pobox.com>
|
|
||||||
Soroush Pour <me at soroushjp.com>
|
|
||||||
Stan Putrya <root.vagner at gmail.com>
|
|
||||||
Stanley Gunawan <gunawan.stanley at gmail.com>
|
|
||||||
Steven Hartland <steven.hartland at multiplay.co.uk>
|
|
||||||
Tan Jinhua <312841925 at qq.com>
|
|
||||||
Thomas Wodarek <wodarekwebpage at gmail.com>
|
|
||||||
Tim Ruffles <timruffles at gmail.com>
|
|
||||||
Tom Jenkinson <tom at tjenkinson.me>
|
|
||||||
Vladimir Kovpak <cn007b at gmail.com>
|
|
||||||
Vladyslav Zhelezniak <zhvladi at gmail.com>
|
|
||||||
Xiangyu Hu <xiangyu.hu at outlook.com>
|
|
||||||
Xiaobing Jiang <s7v7nislands at gmail.com>
|
|
||||||
Xiuming Chen <cc at cxm.cc>
|
|
||||||
Xuehong Chan <chanxuehong at gmail.com>
|
|
||||||
Zhenye Xie <xiezhenye at gmail.com>
|
|
||||||
Zhixin Wen <john.wenzhixin at gmail.com>
|
|
||||||
Ziheng Lyu <zihenglv at gmail.com>
|
|
||||||
|
|
||||||
# Organizations
|
|
||||||
|
|
||||||
Barracuda Networks, Inc.
|
|
||||||
Counting Ltd.
|
|
||||||
DigitalOcean Inc.
|
|
||||||
dyves labs AG
|
|
||||||
Facebook Inc.
|
|
||||||
GitHub Inc.
|
|
||||||
Google Inc.
|
|
||||||
InfoSum Ltd.
|
|
||||||
Keybase Inc.
|
|
||||||
Multiplay Ltd.
|
|
||||||
Percona LLC
|
|
||||||
Pivotal Inc.
|
|
||||||
Stripe Inc.
|
|
||||||
Zendesk Inc.
|
|
||||||
266
vendor/github.com/go-sql-driver/mysql/CHANGELOG.md
generated
vendored
266
vendor/github.com/go-sql-driver/mysql/CHANGELOG.md
generated
vendored
@@ -1,266 +0,0 @@
|
|||||||
## Version 1.7.1 (2023-04-25)
|
|
||||||
|
|
||||||
Changes:
|
|
||||||
|
|
||||||
- bump actions/checkout@v3 and actions/setup-go@v3 (#1375)
|
|
||||||
- Add go1.20 and mariadb10.11 to the testing matrix (#1403)
|
|
||||||
- Increase default maxAllowedPacket size. (#1411)
|
|
||||||
|
|
||||||
Bugfixes:
|
|
||||||
|
|
||||||
- Use SET syntax as specified in the MySQL documentation (#1402)
|
|
||||||
|
|
||||||
|
|
||||||
## Version 1.7 (2022-11-29)
|
|
||||||
|
|
||||||
Changes:
|
|
||||||
|
|
||||||
- Drop support of Go 1.12 (#1211)
|
|
||||||
- Refactoring `(*textRows).readRow` in a more clear way (#1230)
|
|
||||||
- util: Reduce boundary check in escape functions. (#1316)
|
|
||||||
- enhancement for mysqlConn handleAuthResult (#1250)
|
|
||||||
|
|
||||||
New Features:
|
|
||||||
|
|
||||||
- support Is comparison on MySQLError (#1210)
|
|
||||||
- return unsigned in database type name when necessary (#1238)
|
|
||||||
- Add API to express like a --ssl-mode=PREFERRED MySQL client (#1370)
|
|
||||||
- Add SQLState to MySQLError (#1321)
|
|
||||||
|
|
||||||
Bugfixes:
|
|
||||||
|
|
||||||
- Fix parsing 0 year. (#1257)
|
|
||||||
|
|
||||||
|
|
||||||
## Version 1.6 (2021-04-01)
|
|
||||||
|
|
||||||
Changes:
|
|
||||||
|
|
||||||
- Migrate the CI service from travis-ci to GitHub Actions (#1176, #1183, #1190)
|
|
||||||
- `NullTime` is deprecated (#960, #1144)
|
|
||||||
- Reduce allocations when building SET command (#1111)
|
|
||||||
- Performance improvement for time formatting (#1118)
|
|
||||||
- Performance improvement for time parsing (#1098, #1113)
|
|
||||||
|
|
||||||
New Features:
|
|
||||||
|
|
||||||
- Implement `driver.Validator` interface (#1106, #1174)
|
|
||||||
- Support returning `uint64` from `Valuer` in `ConvertValue` (#1143)
|
|
||||||
- Add `json.RawMessage` for converter and prepared statement (#1059)
|
|
||||||
- Interpolate `json.RawMessage` as `string` (#1058)
|
|
||||||
- Implements `CheckNamedValue` (#1090)
|
|
||||||
|
|
||||||
Bugfixes:
|
|
||||||
|
|
||||||
- Stop rounding times (#1121, #1172)
|
|
||||||
- Put zero filler into the SSL handshake packet (#1066)
|
|
||||||
- Fix checking cancelled connections back into the connection pool (#1095)
|
|
||||||
- Fix remove last 0 byte for mysql_old_password when password is empty (#1133)
|
|
||||||
|
|
||||||
|
|
||||||
## Version 1.5 (2020-01-07)
|
|
||||||
|
|
||||||
Changes:
|
|
||||||
|
|
||||||
- Dropped support Go 1.9 and lower (#823, #829, #886, #1016, #1017)
|
|
||||||
- Improve buffer handling (#890)
|
|
||||||
- Document potentially insecure TLS configs (#901)
|
|
||||||
- Use a double-buffering scheme to prevent data races (#943)
|
|
||||||
- Pass uint64 values without converting them to string (#838, #955)
|
|
||||||
- Update collations and make utf8mb4 default (#877, #1054)
|
|
||||||
- Make NullTime compatible with sql.NullTime in Go 1.13+ (#995)
|
|
||||||
- Removed CloudSQL support (#993, #1007)
|
|
||||||
- Add Go Module support (#1003)
|
|
||||||
|
|
||||||
New Features:
|
|
||||||
|
|
||||||
- Implement support of optional TLS (#900)
|
|
||||||
- Check connection liveness (#934, #964, #997, #1048, #1051, #1052)
|
|
||||||
- Implement Connector Interface (#941, #958, #1020, #1035)
|
|
||||||
|
|
||||||
Bugfixes:
|
|
||||||
|
|
||||||
- Mark connections as bad on error during ping (#875)
|
|
||||||
- Mark connections as bad on error during dial (#867)
|
|
||||||
- Fix connection leak caused by rapid context cancellation (#1024)
|
|
||||||
- Mark connections as bad on error during Conn.Prepare (#1030)
|
|
||||||
|
|
||||||
|
|
||||||
## Version 1.4.1 (2018-11-14)
|
|
||||||
|
|
||||||
Bugfixes:
|
|
||||||
|
|
||||||
- Fix TIME format for binary columns (#818)
|
|
||||||
- Fix handling of empty auth plugin names (#835)
|
|
||||||
- Fix caching_sha2_password with empty password (#826)
|
|
||||||
- Fix canceled context broke mysqlConn (#862)
|
|
||||||
- Fix OldAuthSwitchRequest support (#870)
|
|
||||||
- Fix Auth Response packet for cleartext password (#887)
|
|
||||||
|
|
||||||
## Version 1.4 (2018-06-03)
|
|
||||||
|
|
||||||
Changes:
|
|
||||||
|
|
||||||
- Documentation fixes (#530, #535, #567)
|
|
||||||
- Refactoring (#575, #579, #580, #581, #603, #615, #704)
|
|
||||||
- Cache column names (#444)
|
|
||||||
- Sort the DSN parameters in DSNs generated from a config (#637)
|
|
||||||
- Allow native password authentication by default (#644)
|
|
||||||
- Use the default port if it is missing in the DSN (#668)
|
|
||||||
- Removed the `strict` mode (#676)
|
|
||||||
- Do not query `max_allowed_packet` by default (#680)
|
|
||||||
- Dropped support Go 1.6 and lower (#696)
|
|
||||||
- Updated `ConvertValue()` to match the database/sql/driver implementation (#760)
|
|
||||||
- Document the usage of `0000-00-00T00:00:00` as the time.Time zero value (#783)
|
|
||||||
- Improved the compatibility of the authentication system (#807)
|
|
||||||
|
|
||||||
New Features:
|
|
||||||
|
|
||||||
- Multi-Results support (#537)
|
|
||||||
- `rejectReadOnly` DSN option (#604)
|
|
||||||
- `context.Context` support (#608, #612, #627, #761)
|
|
||||||
- Transaction isolation level support (#619, #744)
|
|
||||||
- Read-Only transactions support (#618, #634)
|
|
||||||
- `NewConfig` function which initializes a config with default values (#679)
|
|
||||||
- Implemented the `ColumnType` interfaces (#667, #724)
|
|
||||||
- Support for custom string types in `ConvertValue` (#623)
|
|
||||||
- Implemented `NamedValueChecker`, improving support for uint64 with high bit set (#690, #709, #710)
|
|
||||||
- `caching_sha2_password` authentication plugin support (#794, #800, #801, #802)
|
|
||||||
- Implemented `driver.SessionResetter` (#779)
|
|
||||||
- `sha256_password` authentication plugin support (#808)
|
|
||||||
|
|
||||||
Bugfixes:
|
|
||||||
|
|
||||||
- Use the DSN hostname as TLS default ServerName if `tls=true` (#564, #718)
|
|
||||||
- Fixed LOAD LOCAL DATA INFILE for empty files (#590)
|
|
||||||
- Removed columns definition cache since it sometimes cached invalid data (#592)
|
|
||||||
- Don't mutate registered TLS configs (#600)
|
|
||||||
- Make RegisterTLSConfig concurrency-safe (#613)
|
|
||||||
- Handle missing auth data in the handshake packet correctly (#646)
|
|
||||||
- Do not retry queries when data was written to avoid data corruption (#302, #736)
|
|
||||||
- Cache the connection pointer for error handling before invalidating it (#678)
|
|
||||||
- Fixed imports for appengine/cloudsql (#700)
|
|
||||||
- Fix sending STMT_LONG_DATA for 0 byte data (#734)
|
|
||||||
- Set correct capacity for []bytes read from length-encoded strings (#766)
|
|
||||||
- Make RegisterDial concurrency-safe (#773)
|
|
||||||
|
|
||||||
|
|
||||||
## Version 1.3 (2016-12-01)
|
|
||||||
|
|
||||||
Changes:
|
|
||||||
|
|
||||||
- Go 1.1 is no longer supported
|
|
||||||
- Use decimals fields in MySQL to format time types (#249)
|
|
||||||
- Buffer optimizations (#269)
|
|
||||||
- TLS ServerName defaults to the host (#283)
|
|
||||||
- Refactoring (#400, #410, #437)
|
|
||||||
- Adjusted documentation for second generation CloudSQL (#485)
|
|
||||||
- Documented DSN system var quoting rules (#502)
|
|
||||||
- Made statement.Close() calls idempotent to avoid errors in Go 1.6+ (#512)
|
|
||||||
|
|
||||||
New Features:
|
|
||||||
|
|
||||||
- Enable microsecond resolution on TIME, DATETIME and TIMESTAMP (#249)
|
|
||||||
- Support for returning table alias on Columns() (#289, #359, #382)
|
|
||||||
- Placeholder interpolation, can be actived with the DSN parameter `interpolateParams=true` (#309, #318, #490)
|
|
||||||
- Support for uint64 parameters with high bit set (#332, #345)
|
|
||||||
- Cleartext authentication plugin support (#327)
|
|
||||||
- Exported ParseDSN function and the Config struct (#403, #419, #429)
|
|
||||||
- Read / Write timeouts (#401)
|
|
||||||
- Support for JSON field type (#414)
|
|
||||||
- Support for multi-statements and multi-results (#411, #431)
|
|
||||||
- DSN parameter to set the driver-side max_allowed_packet value manually (#489)
|
|
||||||
- Native password authentication plugin support (#494, #524)
|
|
||||||
|
|
||||||
Bugfixes:
|
|
||||||
|
|
||||||
- Fixed handling of queries without columns and rows (#255)
|
|
||||||
- Fixed a panic when SetKeepAlive() failed (#298)
|
|
||||||
- Handle ERR packets while reading rows (#321)
|
|
||||||
- Fixed reading NULL length-encoded integers in MySQL 5.6+ (#349)
|
|
||||||
- Fixed absolute paths support in LOAD LOCAL DATA INFILE (#356)
|
|
||||||
- Actually zero out bytes in handshake response (#378)
|
|
||||||
- Fixed race condition in registering LOAD DATA INFILE handler (#383)
|
|
||||||
- Fixed tests with MySQL 5.7.9+ (#380)
|
|
||||||
- QueryUnescape TLS config names (#397)
|
|
||||||
- Fixed "broken pipe" error by writing to closed socket (#390)
|
|
||||||
- Fixed LOAD LOCAL DATA INFILE buffering (#424)
|
|
||||||
- Fixed parsing of floats into float64 when placeholders are used (#434)
|
|
||||||
- Fixed DSN tests with Go 1.7+ (#459)
|
|
||||||
- Handle ERR packets while waiting for EOF (#473)
|
|
||||||
- Invalidate connection on error while discarding additional results (#513)
|
|
||||||
- Allow terminating packets of length 0 (#516)
|
|
||||||
|
|
||||||
|
|
||||||
## Version 1.2 (2014-06-03)
|
|
||||||
|
|
||||||
Changes:
|
|
||||||
|
|
||||||
- We switched back to a "rolling release". `go get` installs the current master branch again
|
|
||||||
- Version v1 of the driver will not be maintained anymore. Go 1.0 is no longer supported by this driver
|
|
||||||
- Exported errors to allow easy checking from application code
|
|
||||||
- Enabled TCP Keepalives on TCP connections
|
|
||||||
- Optimized INFILE handling (better buffer size calculation, lazy init, ...)
|
|
||||||
- The DSN parser also checks for a missing separating slash
|
|
||||||
- Faster binary date / datetime to string formatting
|
|
||||||
- Also exported the MySQLWarning type
|
|
||||||
- mysqlConn.Close returns the first error encountered instead of ignoring all errors
|
|
||||||
- writePacket() automatically writes the packet size to the header
|
|
||||||
- readPacket() uses an iterative approach instead of the recursive approach to merge splitted packets
|
|
||||||
|
|
||||||
New Features:
|
|
||||||
|
|
||||||
- `RegisterDial` allows the usage of a custom dial function to establish the network connection
|
|
||||||
- Setting the connection collation is possible with the `collation` DSN parameter. This parameter should be preferred over the `charset` parameter
|
|
||||||
- Logging of critical errors is configurable with `SetLogger`
|
|
||||||
- Google CloudSQL support
|
|
||||||
|
|
||||||
Bugfixes:
|
|
||||||
|
|
||||||
- Allow more than 32 parameters in prepared statements
|
|
||||||
- Various old_password fixes
|
|
||||||
- Fixed TestConcurrent test to pass Go's race detection
|
|
||||||
- Fixed appendLengthEncodedInteger for large numbers
|
|
||||||
- Renamed readLengthEnodedString to readLengthEncodedString and skipLengthEnodedString to skipLengthEncodedString (fixed typo)
|
|
||||||
|
|
||||||
|
|
||||||
## Version 1.1 (2013-11-02)
|
|
||||||
|
|
||||||
Changes:
|
|
||||||
|
|
||||||
- Go-MySQL-Driver now requires Go 1.1
|
|
||||||
- Connections now use the collation `utf8_general_ci` by default. Adding `&charset=UTF8` to the DSN should not be necessary anymore
|
|
||||||
- Made closing rows and connections error tolerant. This allows for example deferring rows.Close() without checking for errors
|
|
||||||
- `[]byte(nil)` is now treated as a NULL value. Before, it was treated like an empty string / `[]byte("")`
|
|
||||||
- DSN parameter values must now be url.QueryEscape'ed. This allows text values to contain special characters, such as '&'.
|
|
||||||
- Use the IO buffer also for writing. This results in zero allocations (by the driver) for most queries
|
|
||||||
- Optimized the buffer for reading
|
|
||||||
- stmt.Query now caches column metadata
|
|
||||||
- New Logo
|
|
||||||
- Changed the copyright header to include all contributors
|
|
||||||
- Improved the LOAD INFILE documentation
|
|
||||||
- The driver struct is now exported to make the driver directly accessible
|
|
||||||
- Refactored the driver tests
|
|
||||||
- Added more benchmarks and moved all to a separate file
|
|
||||||
- Other small refactoring
|
|
||||||
|
|
||||||
New Features:
|
|
||||||
|
|
||||||
- Added *old_passwords* support: Required in some cases, but must be enabled by adding `allowOldPasswords=true` to the DSN since it is insecure
|
|
||||||
- Added a `clientFoundRows` parameter: Return the number of matching rows instead of the number of rows changed on UPDATEs
|
|
||||||
- Added TLS/SSL support: Use a TLS/SSL encrypted connection to the server. Custom TLS configs can be registered and used
|
|
||||||
|
|
||||||
Bugfixes:
|
|
||||||
|
|
||||||
- Fixed MySQL 4.1 support: MySQL 4.1 sends packets with lengths which differ from the specification
|
|
||||||
- Convert to DB timezone when inserting `time.Time`
|
|
||||||
- Splitted packets (more than 16MB) are now merged correctly
|
|
||||||
- Fixed false positive `io.EOF` errors when the data was fully read
|
|
||||||
- Avoid panics on reuse of closed connections
|
|
||||||
- Fixed empty string producing false nil values
|
|
||||||
- Fixed sign byte for positive TIME fields
|
|
||||||
|
|
||||||
|
|
||||||
## Version 1.0 (2013-05-14)
|
|
||||||
|
|
||||||
Initial Release
|
|
||||||
373
vendor/github.com/go-sql-driver/mysql/LICENSE
generated
vendored
373
vendor/github.com/go-sql-driver/mysql/LICENSE
generated
vendored
@@ -1,373 +0,0 @@
|
|||||||
Mozilla Public License Version 2.0
|
|
||||||
==================================
|
|
||||||
|
|
||||||
1. Definitions
|
|
||||||
--------------
|
|
||||||
|
|
||||||
1.1. "Contributor"
|
|
||||||
means each individual or legal entity that creates, contributes to
|
|
||||||
the creation of, or owns Covered Software.
|
|
||||||
|
|
||||||
1.2. "Contributor Version"
|
|
||||||
means the combination of the Contributions of others (if any) used
|
|
||||||
by a Contributor and that particular Contributor's Contribution.
|
|
||||||
|
|
||||||
1.3. "Contribution"
|
|
||||||
means Covered Software of a particular Contributor.
|
|
||||||
|
|
||||||
1.4. "Covered Software"
|
|
||||||
means Source Code Form to which the initial Contributor has attached
|
|
||||||
the notice in Exhibit A, the Executable Form of such Source Code
|
|
||||||
Form, and Modifications of such Source Code Form, in each case
|
|
||||||
including portions thereof.
|
|
||||||
|
|
||||||
1.5. "Incompatible With Secondary Licenses"
|
|
||||||
means
|
|
||||||
|
|
||||||
(a) that the initial Contributor has attached the notice described
|
|
||||||
in Exhibit B to the Covered Software; or
|
|
||||||
|
|
||||||
(b) that the Covered Software was made available under the terms of
|
|
||||||
version 1.1 or earlier of the License, but not also under the
|
|
||||||
terms of a Secondary License.
|
|
||||||
|
|
||||||
1.6. "Executable Form"
|
|
||||||
means any form of the work other than Source Code Form.
|
|
||||||
|
|
||||||
1.7. "Larger Work"
|
|
||||||
means a work that combines Covered Software with other material, in
|
|
||||||
a separate file or files, that is not Covered Software.
|
|
||||||
|
|
||||||
1.8. "License"
|
|
||||||
means this document.
|
|
||||||
|
|
||||||
1.9. "Licensable"
|
|
||||||
means having the right to grant, to the maximum extent possible,
|
|
||||||
whether at the time of the initial grant or subsequently, any and
|
|
||||||
all of the rights conveyed by this License.
|
|
||||||
|
|
||||||
1.10. "Modifications"
|
|
||||||
means any of the following:
|
|
||||||
|
|
||||||
(a) any file in Source Code Form that results from an addition to,
|
|
||||||
deletion from, or modification of the contents of Covered
|
|
||||||
Software; or
|
|
||||||
|
|
||||||
(b) any new file in Source Code Form that contains any Covered
|
|
||||||
Software.
|
|
||||||
|
|
||||||
1.11. "Patent Claims" of a Contributor
|
|
||||||
means any patent claim(s), including without limitation, method,
|
|
||||||
process, and apparatus claims, in any patent Licensable by such
|
|
||||||
Contributor that would be infringed, but for the grant of the
|
|
||||||
License, by the making, using, selling, offering for sale, having
|
|
||||||
made, import, or transfer of either its Contributions or its
|
|
||||||
Contributor Version.
|
|
||||||
|
|
||||||
1.12. "Secondary License"
|
|
||||||
means either the GNU General Public License, Version 2.0, the GNU
|
|
||||||
Lesser General Public License, Version 2.1, the GNU Affero General
|
|
||||||
Public License, Version 3.0, or any later versions of those
|
|
||||||
licenses.
|
|
||||||
|
|
||||||
1.13. "Source Code Form"
|
|
||||||
means the form of the work preferred for making modifications.
|
|
||||||
|
|
||||||
1.14. "You" (or "Your")
|
|
||||||
means an individual or a legal entity exercising rights under this
|
|
||||||
License. For legal entities, "You" includes any entity that
|
|
||||||
controls, is controlled by, or is under common control with You. For
|
|
||||||
purposes of this definition, "control" means (a) the power, direct
|
|
||||||
or indirect, to cause the direction or management of such entity,
|
|
||||||
whether by contract or otherwise, or (b) ownership of more than
|
|
||||||
fifty percent (50%) of the outstanding shares or beneficial
|
|
||||||
ownership of such entity.
|
|
||||||
|
|
||||||
2. License Grants and Conditions
|
|
||||||
--------------------------------
|
|
||||||
|
|
||||||
2.1. Grants
|
|
||||||
|
|
||||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
|
||||||
non-exclusive license:
|
|
||||||
|
|
||||||
(a) under intellectual property rights (other than patent or trademark)
|
|
||||||
Licensable by such Contributor to use, reproduce, make available,
|
|
||||||
modify, display, perform, distribute, and otherwise exploit its
|
|
||||||
Contributions, either on an unmodified basis, with Modifications, or
|
|
||||||
as part of a Larger Work; and
|
|
||||||
|
|
||||||
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
|
||||||
for sale, have made, import, and otherwise transfer either its
|
|
||||||
Contributions or its Contributor Version.
|
|
||||||
|
|
||||||
2.2. Effective Date
|
|
||||||
|
|
||||||
The licenses granted in Section 2.1 with respect to any Contribution
|
|
||||||
become effective for each Contribution on the date the Contributor first
|
|
||||||
distributes such Contribution.
|
|
||||||
|
|
||||||
2.3. Limitations on Grant Scope
|
|
||||||
|
|
||||||
The licenses granted in this Section 2 are the only rights granted under
|
|
||||||
this License. No additional rights or licenses will be implied from the
|
|
||||||
distribution or licensing of Covered Software under this License.
|
|
||||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
|
||||||
Contributor:
|
|
||||||
|
|
||||||
(a) for any code that a Contributor has removed from Covered Software;
|
|
||||||
or
|
|
||||||
|
|
||||||
(b) for infringements caused by: (i) Your and any other third party's
|
|
||||||
modifications of Covered Software, or (ii) the combination of its
|
|
||||||
Contributions with other software (except as part of its Contributor
|
|
||||||
Version); or
|
|
||||||
|
|
||||||
(c) under Patent Claims infringed by Covered Software in the absence of
|
|
||||||
its Contributions.
|
|
||||||
|
|
||||||
This License does not grant any rights in the trademarks, service marks,
|
|
||||||
or logos of any Contributor (except as may be necessary to comply with
|
|
||||||
the notice requirements in Section 3.4).
|
|
||||||
|
|
||||||
2.4. Subsequent Licenses
|
|
||||||
|
|
||||||
No Contributor makes additional grants as a result of Your choice to
|
|
||||||
distribute the Covered Software under a subsequent version of this
|
|
||||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
|
||||||
permitted under the terms of Section 3.3).
|
|
||||||
|
|
||||||
2.5. Representation
|
|
||||||
|
|
||||||
Each Contributor represents that the Contributor believes its
|
|
||||||
Contributions are its original creation(s) or it has sufficient rights
|
|
||||||
to grant the rights to its Contributions conveyed by this License.
|
|
||||||
|
|
||||||
2.6. Fair Use
|
|
||||||
|
|
||||||
This License is not intended to limit any rights You have under
|
|
||||||
applicable copyright doctrines of fair use, fair dealing, or other
|
|
||||||
equivalents.
|
|
||||||
|
|
||||||
2.7. Conditions
|
|
||||||
|
|
||||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
|
||||||
in Section 2.1.
|
|
||||||
|
|
||||||
3. Responsibilities
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
3.1. Distribution of Source Form
|
|
||||||
|
|
||||||
All distribution of Covered Software in Source Code Form, including any
|
|
||||||
Modifications that You create or to which You contribute, must be under
|
|
||||||
the terms of this License. You must inform recipients that the Source
|
|
||||||
Code Form of the Covered Software is governed by the terms of this
|
|
||||||
License, and how they can obtain a copy of this License. You may not
|
|
||||||
attempt to alter or restrict the recipients' rights in the Source Code
|
|
||||||
Form.
|
|
||||||
|
|
||||||
3.2. Distribution of Executable Form
|
|
||||||
|
|
||||||
If You distribute Covered Software in Executable Form then:
|
|
||||||
|
|
||||||
(a) such Covered Software must also be made available in Source Code
|
|
||||||
Form, as described in Section 3.1, and You must inform recipients of
|
|
||||||
the Executable Form how they can obtain a copy of such Source Code
|
|
||||||
Form by reasonable means in a timely manner, at a charge no more
|
|
||||||
than the cost of distribution to the recipient; and
|
|
||||||
|
|
||||||
(b) You may distribute such Executable Form under the terms of this
|
|
||||||
License, or sublicense it under different terms, provided that the
|
|
||||||
license for the Executable Form does not attempt to limit or alter
|
|
||||||
the recipients' rights in the Source Code Form under this License.
|
|
||||||
|
|
||||||
3.3. Distribution of a Larger Work
|
|
||||||
|
|
||||||
You may create and distribute a Larger Work under terms of Your choice,
|
|
||||||
provided that You also comply with the requirements of this License for
|
|
||||||
the Covered Software. If the Larger Work is a combination of Covered
|
|
||||||
Software with a work governed by one or more Secondary Licenses, and the
|
|
||||||
Covered Software is not Incompatible With Secondary Licenses, this
|
|
||||||
License permits You to additionally distribute such Covered Software
|
|
||||||
under the terms of such Secondary License(s), so that the recipient of
|
|
||||||
the Larger Work may, at their option, further distribute the Covered
|
|
||||||
Software under the terms of either this License or such Secondary
|
|
||||||
License(s).
|
|
||||||
|
|
||||||
3.4. Notices
|
|
||||||
|
|
||||||
You may not remove or alter the substance of any license notices
|
|
||||||
(including copyright notices, patent notices, disclaimers of warranty,
|
|
||||||
or limitations of liability) contained within the Source Code Form of
|
|
||||||
the Covered Software, except that You may alter any license notices to
|
|
||||||
the extent required to remedy known factual inaccuracies.
|
|
||||||
|
|
||||||
3.5. Application of Additional Terms
|
|
||||||
|
|
||||||
You may choose to offer, and to charge a fee for, warranty, support,
|
|
||||||
indemnity or liability obligations to one or more recipients of Covered
|
|
||||||
Software. However, You may do so only on Your own behalf, and not on
|
|
||||||
behalf of any Contributor. You must make it absolutely clear that any
|
|
||||||
such warranty, support, indemnity, or liability obligation is offered by
|
|
||||||
You alone, and You hereby agree to indemnify every Contributor for any
|
|
||||||
liability incurred by such Contributor as a result of warranty, support,
|
|
||||||
indemnity or liability terms You offer. You may include additional
|
|
||||||
disclaimers of warranty and limitations of liability specific to any
|
|
||||||
jurisdiction.
|
|
||||||
|
|
||||||
4. Inability to Comply Due to Statute or Regulation
|
|
||||||
---------------------------------------------------
|
|
||||||
|
|
||||||
If it is impossible for You to comply with any of the terms of this
|
|
||||||
License with respect to some or all of the Covered Software due to
|
|
||||||
statute, judicial order, or regulation then You must: (a) comply with
|
|
||||||
the terms of this License to the maximum extent possible; and (b)
|
|
||||||
describe the limitations and the code they affect. Such description must
|
|
||||||
be placed in a text file included with all distributions of the Covered
|
|
||||||
Software under this License. Except to the extent prohibited by statute
|
|
||||||
or regulation, such description must be sufficiently detailed for a
|
|
||||||
recipient of ordinary skill to be able to understand it.
|
|
||||||
|
|
||||||
5. Termination
|
|
||||||
--------------
|
|
||||||
|
|
||||||
5.1. The rights granted under this License will terminate automatically
|
|
||||||
if You fail to comply with any of its terms. However, if You become
|
|
||||||
compliant, then the rights granted under this License from a particular
|
|
||||||
Contributor are reinstated (a) provisionally, unless and until such
|
|
||||||
Contributor explicitly and finally terminates Your grants, and (b) on an
|
|
||||||
ongoing basis, if such Contributor fails to notify You of the
|
|
||||||
non-compliance by some reasonable means prior to 60 days after You have
|
|
||||||
come back into compliance. Moreover, Your grants from a particular
|
|
||||||
Contributor are reinstated on an ongoing basis if such Contributor
|
|
||||||
notifies You of the non-compliance by some reasonable means, this is the
|
|
||||||
first time You have received notice of non-compliance with this License
|
|
||||||
from such Contributor, and You become compliant prior to 30 days after
|
|
||||||
Your receipt of the notice.
|
|
||||||
|
|
||||||
5.2. If You initiate litigation against any entity by asserting a patent
|
|
||||||
infringement claim (excluding declaratory judgment actions,
|
|
||||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
|
||||||
directly or indirectly infringes any patent, then the rights granted to
|
|
||||||
You by any and all Contributors for the Covered Software under Section
|
|
||||||
2.1 of this License shall terminate.
|
|
||||||
|
|
||||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
|
||||||
end user license agreements (excluding distributors and resellers) which
|
|
||||||
have been validly granted by You or Your distributors under this License
|
|
||||||
prior to termination shall survive termination.
|
|
||||||
|
|
||||||
************************************************************************
|
|
||||||
* *
|
|
||||||
* 6. Disclaimer of Warranty *
|
|
||||||
* ------------------------- *
|
|
||||||
* *
|
|
||||||
* Covered Software is provided under this License on an "as is" *
|
|
||||||
* basis, without warranty of any kind, either expressed, implied, or *
|
|
||||||
* statutory, including, without limitation, warranties that the *
|
|
||||||
* Covered Software is free of defects, merchantable, fit for a *
|
|
||||||
* particular purpose or non-infringing. The entire risk as to the *
|
|
||||||
* quality and performance of the Covered Software is with You. *
|
|
||||||
* Should any Covered Software prove defective in any respect, You *
|
|
||||||
* (not any Contributor) assume the cost of any necessary servicing, *
|
|
||||||
* repair, or correction. This disclaimer of warranty constitutes an *
|
|
||||||
* essential part of this License. No use of any Covered Software is *
|
|
||||||
* authorized under this License except under this disclaimer. *
|
|
||||||
* *
|
|
||||||
************************************************************************
|
|
||||||
|
|
||||||
************************************************************************
|
|
||||||
* *
|
|
||||||
* 7. Limitation of Liability *
|
|
||||||
* -------------------------- *
|
|
||||||
* *
|
|
||||||
* Under no circumstances and under no legal theory, whether tort *
|
|
||||||
* (including negligence), contract, or otherwise, shall any *
|
|
||||||
* Contributor, or anyone who distributes Covered Software as *
|
|
||||||
* permitted above, be liable to You for any direct, indirect, *
|
|
||||||
* special, incidental, or consequential damages of any character *
|
|
||||||
* including, without limitation, damages for lost profits, loss of *
|
|
||||||
* goodwill, work stoppage, computer failure or malfunction, or any *
|
|
||||||
* and all other commercial damages or losses, even if such party *
|
|
||||||
* shall have been informed of the possibility of such damages. This *
|
|
||||||
* limitation of liability shall not apply to liability for death or *
|
|
||||||
* personal injury resulting from such party's negligence to the *
|
|
||||||
* extent applicable law prohibits such limitation. Some *
|
|
||||||
* jurisdictions do not allow the exclusion or limitation of *
|
|
||||||
* incidental or consequential damages, so this exclusion and *
|
|
||||||
* limitation may not apply to You. *
|
|
||||||
* *
|
|
||||||
************************************************************************
|
|
||||||
|
|
||||||
8. Litigation
|
|
||||||
-------------
|
|
||||||
|
|
||||||
Any litigation relating to this License may be brought only in the
|
|
||||||
courts of a jurisdiction where the defendant maintains its principal
|
|
||||||
place of business and such litigation shall be governed by laws of that
|
|
||||||
jurisdiction, without reference to its conflict-of-law provisions.
|
|
||||||
Nothing in this Section shall prevent a party's ability to bring
|
|
||||||
cross-claims or counter-claims.
|
|
||||||
|
|
||||||
9. Miscellaneous
|
|
||||||
----------------
|
|
||||||
|
|
||||||
This License represents the complete agreement concerning the subject
|
|
||||||
matter hereof. If any provision of this License is held to be
|
|
||||||
unenforceable, such provision shall be reformed only to the extent
|
|
||||||
necessary to make it enforceable. Any law or regulation which provides
|
|
||||||
that the language of a contract shall be construed against the drafter
|
|
||||||
shall not be used to construe this License against a Contributor.
|
|
||||||
|
|
||||||
10. Versions of the License
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
10.1. New Versions
|
|
||||||
|
|
||||||
Mozilla Foundation is the license steward. Except as provided in Section
|
|
||||||
10.3, no one other than the license steward has the right to modify or
|
|
||||||
publish new versions of this License. Each version will be given a
|
|
||||||
distinguishing version number.
|
|
||||||
|
|
||||||
10.2. Effect of New Versions
|
|
||||||
|
|
||||||
You may distribute the Covered Software under the terms of the version
|
|
||||||
of the License under which You originally received the Covered Software,
|
|
||||||
or under the terms of any subsequent version published by the license
|
|
||||||
steward.
|
|
||||||
|
|
||||||
10.3. Modified Versions
|
|
||||||
|
|
||||||
If you create software not governed by this License, and you want to
|
|
||||||
create a new license for such software, you may create and use a
|
|
||||||
modified version of this License if you rename the license and remove
|
|
||||||
any references to the name of the license steward (except to note that
|
|
||||||
such modified license differs from this License).
|
|
||||||
|
|
||||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
|
||||||
Licenses
|
|
||||||
|
|
||||||
If You choose to distribute Source Code Form that is Incompatible With
|
|
||||||
Secondary Licenses under the terms of this version of the License, the
|
|
||||||
notice described in Exhibit B of this License must be attached.
|
|
||||||
|
|
||||||
Exhibit A - Source Code Form License Notice
|
|
||||||
-------------------------------------------
|
|
||||||
|
|
||||||
This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
|
|
||||||
If it is not possible or desirable to put the notice in a particular
|
|
||||||
file, then You may include the notice in a location (such as a LICENSE
|
|
||||||
file in a relevant directory) where a recipient would be likely to look
|
|
||||||
for such a notice.
|
|
||||||
|
|
||||||
You may add additional accurate notices of copyright ownership.
|
|
||||||
|
|
||||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
|
||||||
---------------------------------------------------------
|
|
||||||
|
|
||||||
This Source Code Form is "Incompatible With Secondary Licenses", as
|
|
||||||
defined by the Mozilla Public License, v. 2.0.
|
|
||||||
531
vendor/github.com/go-sql-driver/mysql/README.md
generated
vendored
531
vendor/github.com/go-sql-driver/mysql/README.md
generated
vendored
@@ -1,531 +0,0 @@
|
|||||||
# Go-MySQL-Driver
|
|
||||||
|
|
||||||
A MySQL-Driver for Go's [database/sql](https://golang.org/pkg/database/sql/) package
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
---------------------------------------
|
|
||||||
* [Features](#features)
|
|
||||||
* [Requirements](#requirements)
|
|
||||||
* [Installation](#installation)
|
|
||||||
* [Usage](#usage)
|
|
||||||
* [DSN (Data Source Name)](#dsn-data-source-name)
|
|
||||||
* [Password](#password)
|
|
||||||
* [Protocol](#protocol)
|
|
||||||
* [Address](#address)
|
|
||||||
* [Parameters](#parameters)
|
|
||||||
* [Examples](#examples)
|
|
||||||
* [Connection pool and timeouts](#connection-pool-and-timeouts)
|
|
||||||
* [context.Context Support](#contextcontext-support)
|
|
||||||
* [ColumnType Support](#columntype-support)
|
|
||||||
* [LOAD DATA LOCAL INFILE support](#load-data-local-infile-support)
|
|
||||||
* [time.Time support](#timetime-support)
|
|
||||||
* [Unicode support](#unicode-support)
|
|
||||||
* [Testing / Development](#testing--development)
|
|
||||||
* [License](#license)
|
|
||||||
|
|
||||||
---------------------------------------
|
|
||||||
|
|
||||||
## Features
|
|
||||||
* Lightweight and [fast](https://github.com/go-sql-driver/sql-benchmark "golang MySQL-Driver performance")
|
|
||||||
* Native Go implementation. No C-bindings, just pure Go
|
|
||||||
* Connections over TCP/IPv4, TCP/IPv6, Unix domain sockets or [custom protocols](https://godoc.org/github.com/go-sql-driver/mysql#DialFunc)
|
|
||||||
* Automatic handling of broken connections
|
|
||||||
* Automatic Connection Pooling *(by database/sql package)*
|
|
||||||
* Supports queries larger than 16MB
|
|
||||||
* Full [`sql.RawBytes`](https://golang.org/pkg/database/sql/#RawBytes) support.
|
|
||||||
* Intelligent `LONG DATA` handling in prepared statements
|
|
||||||
* Secure `LOAD DATA LOCAL INFILE` support with file allowlisting and `io.Reader` support
|
|
||||||
* Optional `time.Time` parsing
|
|
||||||
* Optional placeholder interpolation
|
|
||||||
|
|
||||||
## Requirements
|
|
||||||
* Go 1.13 or higher. We aim to support the 3 latest versions of Go.
|
|
||||||
* MySQL (4.1+), MariaDB, Percona Server, Google CloudSQL or Sphinx (2.2.3+)
|
|
||||||
|
|
||||||
---------------------------------------
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
Simple install the package to your [$GOPATH](https://github.com/golang/go/wiki/GOPATH "GOPATH") with the [go tool](https://golang.org/cmd/go/ "go command") from shell:
|
|
||||||
```bash
|
|
||||||
$ go get -u github.com/go-sql-driver/mysql
|
|
||||||
```
|
|
||||||
Make sure [Git is installed](https://git-scm.com/downloads) on your machine and in your system's `PATH`.
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
_Go MySQL Driver_ is an implementation of Go's `database/sql/driver` interface. You only need to import the driver and can use the full [`database/sql`](https://golang.org/pkg/database/sql/) API then.
|
|
||||||
|
|
||||||
Use `mysql` as `driverName` and a valid [DSN](#dsn-data-source-name) as `dataSourceName`:
|
|
||||||
|
|
||||||
```go
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
_ "github.com/go-sql-driver/mysql"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ...
|
|
||||||
|
|
||||||
db, err := sql.Open("mysql", "user:password@/dbname")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
// See "Important settings" section.
|
|
||||||
db.SetConnMaxLifetime(time.Minute * 3)
|
|
||||||
db.SetMaxOpenConns(10)
|
|
||||||
db.SetMaxIdleConns(10)
|
|
||||||
```
|
|
||||||
|
|
||||||
[Examples are available in our Wiki](https://github.com/go-sql-driver/mysql/wiki/Examples "Go-MySQL-Driver Examples").
|
|
||||||
|
|
||||||
### Important settings
|
|
||||||
|
|
||||||
`db.SetConnMaxLifetime()` is required to ensure connections are closed by the driver safely before connection is closed by MySQL server, OS, or other middlewares. Since some middlewares close idle connections by 5 minutes, we recommend timeout shorter than 5 minutes. This setting helps load balancing and changing system variables too.
|
|
||||||
|
|
||||||
`db.SetMaxOpenConns()` is highly recommended to limit the number of connection used by the application. There is no recommended limit number because it depends on application and MySQL server.
|
|
||||||
|
|
||||||
`db.SetMaxIdleConns()` is recommended to be set same to `db.SetMaxOpenConns()`. When it is smaller than `SetMaxOpenConns()`, connections can be opened and closed much more frequently than you expect. Idle connections can be closed by the `db.SetConnMaxLifetime()`. If you want to close idle connections more rapidly, you can use `db.SetConnMaxIdleTime()` since Go 1.15.
|
|
||||||
|
|
||||||
|
|
||||||
### DSN (Data Source Name)
|
|
||||||
|
|
||||||
The Data Source Name has a common format, like e.g. [PEAR DB](http://pear.php.net/manual/en/package.database.db.intro-dsn.php) uses it, but without type-prefix (optional parts marked by squared brackets):
|
|
||||||
```
|
|
||||||
[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN]
|
|
||||||
```
|
|
||||||
|
|
||||||
A DSN in its fullest form:
|
|
||||||
```
|
|
||||||
username:password@protocol(address)/dbname?param=value
|
|
||||||
```
|
|
||||||
|
|
||||||
Except for the databasename, all values are optional. So the minimal DSN is:
|
|
||||||
```
|
|
||||||
/dbname
|
|
||||||
```
|
|
||||||
|
|
||||||
If you do not want to preselect a database, leave `dbname` empty:
|
|
||||||
```
|
|
||||||
/
|
|
||||||
```
|
|
||||||
This has the same effect as an empty DSN string:
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
Alternatively, [Config.FormatDSN](https://godoc.org/github.com/go-sql-driver/mysql#Config.FormatDSN) can be used to create a DSN string by filling a struct.
|
|
||||||
|
|
||||||
#### Password
|
|
||||||
Passwords can consist of any character. Escaping is **not** necessary.
|
|
||||||
|
|
||||||
#### Protocol
|
|
||||||
See [net.Dial](https://golang.org/pkg/net/#Dial) for more information which networks are available.
|
|
||||||
In general you should use an Unix domain socket if available and TCP otherwise for best performance.
|
|
||||||
|
|
||||||
#### Address
|
|
||||||
For TCP and UDP networks, addresses have the form `host[:port]`.
|
|
||||||
If `port` is omitted, the default port will be used.
|
|
||||||
If `host` is a literal IPv6 address, it must be enclosed in square brackets.
|
|
||||||
The functions [net.JoinHostPort](https://golang.org/pkg/net/#JoinHostPort) and [net.SplitHostPort](https://golang.org/pkg/net/#SplitHostPort) manipulate addresses in this form.
|
|
||||||
|
|
||||||
For Unix domain sockets the address is the absolute path to the MySQL-Server-socket, e.g. `/var/run/mysqld/mysqld.sock` or `/tmp/mysql.sock`.
|
|
||||||
|
|
||||||
#### Parameters
|
|
||||||
*Parameters are case-sensitive!*
|
|
||||||
|
|
||||||
Notice that any of `true`, `TRUE`, `True` or `1` is accepted to stand for a true boolean value. Not surprisingly, false can be specified as any of: `false`, `FALSE`, `False` or `0`.
|
|
||||||
|
|
||||||
##### `allowAllFiles`
|
|
||||||
|
|
||||||
```
|
|
||||||
Type: bool
|
|
||||||
Valid Values: true, false
|
|
||||||
Default: false
|
|
||||||
```
|
|
||||||
|
|
||||||
`allowAllFiles=true` disables the file allowlist for `LOAD DATA LOCAL INFILE` and allows *all* files.
|
|
||||||
[*Might be insecure!*](http://dev.mysql.com/doc/refman/5.7/en/load-data-local.html)
|
|
||||||
|
|
||||||
##### `allowCleartextPasswords`
|
|
||||||
|
|
||||||
```
|
|
||||||
Type: bool
|
|
||||||
Valid Values: true, false
|
|
||||||
Default: false
|
|
||||||
```
|
|
||||||
|
|
||||||
`allowCleartextPasswords=true` allows using the [cleartext client side plugin](https://dev.mysql.com/doc/en/cleartext-pluggable-authentication.html) if required by an account, such as one defined with the [PAM authentication plugin](http://dev.mysql.com/doc/en/pam-authentication-plugin.html). Sending passwords in clear text may be a security problem in some configurations. To avoid problems if there is any possibility that the password would be intercepted, clients should connect to MySQL Server using a method that protects the password. Possibilities include [TLS / SSL](#tls), IPsec, or a private network.
|
|
||||||
|
|
||||||
|
|
||||||
##### `allowFallbackToPlaintext`
|
|
||||||
|
|
||||||
```
|
|
||||||
Type: bool
|
|
||||||
Valid Values: true, false
|
|
||||||
Default: false
|
|
||||||
```
|
|
||||||
|
|
||||||
`allowFallbackToPlaintext=true` acts like a `--ssl-mode=PREFERRED` MySQL client as described in [Command Options for Connecting to the Server](https://dev.mysql.com/doc/refman/5.7/en/connection-options.html#option_general_ssl-mode)
|
|
||||||
|
|
||||||
##### `allowNativePasswords`
|
|
||||||
|
|
||||||
```
|
|
||||||
Type: bool
|
|
||||||
Valid Values: true, false
|
|
||||||
Default: true
|
|
||||||
```
|
|
||||||
`allowNativePasswords=false` disallows the usage of MySQL native password method.
|
|
||||||
|
|
||||||
##### `allowOldPasswords`
|
|
||||||
|
|
||||||
```
|
|
||||||
Type: bool
|
|
||||||
Valid Values: true, false
|
|
||||||
Default: false
|
|
||||||
```
|
|
||||||
`allowOldPasswords=true` allows the usage of the insecure old password method. This should be avoided, but is necessary in some cases. See also [the old_passwords wiki page](https://github.com/go-sql-driver/mysql/wiki/old_passwords).
|
|
||||||
|
|
||||||
##### `charset`
|
|
||||||
|
|
||||||
```
|
|
||||||
Type: string
|
|
||||||
Valid Values: <name>
|
|
||||||
Default: none
|
|
||||||
```
|
|
||||||
|
|
||||||
Sets the charset used for client-server interaction (`"SET NAMES <value>"`). If multiple charsets are set (separated by a comma), the following charset is used if setting the charset failes. This enables for example support for `utf8mb4` ([introduced in MySQL 5.5.3](http://dev.mysql.com/doc/refman/5.5/en/charset-unicode-utf8mb4.html)) with fallback to `utf8` for older servers (`charset=utf8mb4,utf8`).
|
|
||||||
|
|
||||||
Usage of the `charset` parameter is discouraged because it issues additional queries to the server.
|
|
||||||
Unless you need the fallback behavior, please use `collation` instead.
|
|
||||||
|
|
||||||
##### `checkConnLiveness`
|
|
||||||
|
|
||||||
```
|
|
||||||
Type: bool
|
|
||||||
Valid Values: true, false
|
|
||||||
Default: true
|
|
||||||
```
|
|
||||||
|
|
||||||
On supported platforms connections retrieved from the connection pool are checked for liveness before using them. If the check fails, the respective connection is marked as bad and the query retried with another connection.
|
|
||||||
`checkConnLiveness=false` disables this liveness check of connections.
|
|
||||||
|
|
||||||
##### `collation`
|
|
||||||
|
|
||||||
```
|
|
||||||
Type: string
|
|
||||||
Valid Values: <name>
|
|
||||||
Default: utf8mb4_general_ci
|
|
||||||
```
|
|
||||||
|
|
||||||
Sets the collation used for client-server interaction on connection. In contrast to `charset`, `collation` does not issue additional queries. If the specified collation is unavailable on the target server, the connection will fail.
|
|
||||||
|
|
||||||
A list of valid charsets for a server is retrievable with `SHOW COLLATION`.
|
|
||||||
|
|
||||||
The default collation (`utf8mb4_general_ci`) is supported from MySQL 5.5. You should use an older collation (e.g. `utf8_general_ci`) for older MySQL.
|
|
||||||
|
|
||||||
Collations for charset "ucs2", "utf16", "utf16le", and "utf32" can not be used ([ref](https://dev.mysql.com/doc/refman/5.7/en/charset-connection.html#charset-connection-impermissible-client-charset)).
|
|
||||||
|
|
||||||
|
|
||||||
##### `clientFoundRows`
|
|
||||||
|
|
||||||
```
|
|
||||||
Type: bool
|
|
||||||
Valid Values: true, false
|
|
||||||
Default: false
|
|
||||||
```
|
|
||||||
|
|
||||||
`clientFoundRows=true` causes an UPDATE to return the number of matching rows instead of the number of rows changed.
|
|
||||||
|
|
||||||
##### `columnsWithAlias`
|
|
||||||
|
|
||||||
```
|
|
||||||
Type: bool
|
|
||||||
Valid Values: true, false
|
|
||||||
Default: false
|
|
||||||
```
|
|
||||||
|
|
||||||
When `columnsWithAlias` is true, calls to `sql.Rows.Columns()` will return the table alias and the column name separated by a dot. For example:
|
|
||||||
|
|
||||||
```
|
|
||||||
SELECT u.id FROM users as u
|
|
||||||
```
|
|
||||||
|
|
||||||
will return `u.id` instead of just `id` if `columnsWithAlias=true`.
|
|
||||||
|
|
||||||
##### `interpolateParams`
|
|
||||||
|
|
||||||
```
|
|
||||||
Type: bool
|
|
||||||
Valid Values: true, false
|
|
||||||
Default: false
|
|
||||||
```
|
|
||||||
|
|
||||||
If `interpolateParams` is true, placeholders (`?`) in calls to `db.Query()` and `db.Exec()` are interpolated into a single query string with given parameters. This reduces the number of roundtrips, since the driver has to prepare a statement, execute it with given parameters and close the statement again with `interpolateParams=false`.
|
|
||||||
|
|
||||||
*This can not be used together with the multibyte encodings BIG5, CP932, GB2312, GBK or SJIS. These are rejected as they may [introduce a SQL injection vulnerability](http://stackoverflow.com/a/12118602/3430118)!*
|
|
||||||
|
|
||||||
##### `loc`
|
|
||||||
|
|
||||||
```
|
|
||||||
Type: string
|
|
||||||
Valid Values: <escaped name>
|
|
||||||
Default: UTC
|
|
||||||
```
|
|
||||||
|
|
||||||
Sets the location for time.Time values (when using `parseTime=true`). *"Local"* sets the system's location. See [time.LoadLocation](https://golang.org/pkg/time/#LoadLocation) for details.
|
|
||||||
|
|
||||||
Note that this sets the location for time.Time values but does not change MySQL's [time_zone setting](https://dev.mysql.com/doc/refman/5.5/en/time-zone-support.html). For that see the [time_zone system variable](#system-variables), which can also be set as a DSN parameter.
|
|
||||||
|
|
||||||
Please keep in mind, that param values must be [url.QueryEscape](https://golang.org/pkg/net/url/#QueryEscape)'ed. Alternatively you can manually replace the `/` with `%2F`. For example `US/Pacific` would be `loc=US%2FPacific`.
|
|
||||||
|
|
||||||
##### `maxAllowedPacket`
|
|
||||||
```
|
|
||||||
Type: decimal number
|
|
||||||
Default: 64*1024*1024
|
|
||||||
```
|
|
||||||
|
|
||||||
Max packet size allowed in bytes. The default value is 64 MiB and should be adjusted to match the server settings. `maxAllowedPacket=0` can be used to automatically fetch the `max_allowed_packet` variable from server *on every connection*.
|
|
||||||
|
|
||||||
##### `multiStatements`
|
|
||||||
|
|
||||||
```
|
|
||||||
Type: bool
|
|
||||||
Valid Values: true, false
|
|
||||||
Default: false
|
|
||||||
```
|
|
||||||
|
|
||||||
Allow multiple statements in one query. While this allows batch queries, it also greatly increases the risk of SQL injections. Only the result of the first query is returned, all other results are silently discarded.
|
|
||||||
|
|
||||||
When `multiStatements` is used, `?` parameters must only be used in the first statement.
|
|
||||||
|
|
||||||
##### `parseTime`
|
|
||||||
|
|
||||||
```
|
|
||||||
Type: bool
|
|
||||||
Valid Values: true, false
|
|
||||||
Default: false
|
|
||||||
```
|
|
||||||
|
|
||||||
`parseTime=true` changes the output type of `DATE` and `DATETIME` values to `time.Time` instead of `[]byte` / `string`
|
|
||||||
The date or datetime like `0000-00-00 00:00:00` is converted into zero value of `time.Time`.
|
|
||||||
|
|
||||||
|
|
||||||
##### `readTimeout`
|
|
||||||
|
|
||||||
```
|
|
||||||
Type: duration
|
|
||||||
Default: 0
|
|
||||||
```
|
|
||||||
|
|
||||||
I/O read timeout. The value must be a decimal number with a unit suffix (*"ms"*, *"s"*, *"m"*, *"h"*), such as *"30s"*, *"0.5m"* or *"1m30s"*.
|
|
||||||
|
|
||||||
##### `rejectReadOnly`
|
|
||||||
|
|
||||||
```
|
|
||||||
Type: bool
|
|
||||||
Valid Values: true, false
|
|
||||||
Default: false
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
`rejectReadOnly=true` causes the driver to reject read-only connections. This
|
|
||||||
is for a possible race condition during an automatic failover, where the mysql
|
|
||||||
client gets connected to a read-only replica after the failover.
|
|
||||||
|
|
||||||
Note that this should be a fairly rare case, as an automatic failover normally
|
|
||||||
happens when the primary is down, and the race condition shouldn't happen
|
|
||||||
unless it comes back up online as soon as the failover is kicked off. On the
|
|
||||||
other hand, when this happens, a MySQL application can get stuck on a
|
|
||||||
read-only connection until restarted. It is however fairly easy to reproduce,
|
|
||||||
for example, using a manual failover on AWS Aurora's MySQL-compatible cluster.
|
|
||||||
|
|
||||||
If you are not relying on read-only transactions to reject writes that aren't
|
|
||||||
supposed to happen, setting this on some MySQL providers (such as AWS Aurora)
|
|
||||||
is safer for failovers.
|
|
||||||
|
|
||||||
Note that ERROR 1290 can be returned for a `read-only` server and this option will
|
|
||||||
cause a retry for that error. However the same error number is used for some
|
|
||||||
other cases. You should ensure your application will never cause an ERROR 1290
|
|
||||||
except for `read-only` mode when enabling this option.
|
|
||||||
|
|
||||||
|
|
||||||
##### `serverPubKey`
|
|
||||||
|
|
||||||
```
|
|
||||||
Type: string
|
|
||||||
Valid Values: <name>
|
|
||||||
Default: none
|
|
||||||
```
|
|
||||||
|
|
||||||
Server public keys can be registered with [`mysql.RegisterServerPubKey`](https://godoc.org/github.com/go-sql-driver/mysql#RegisterServerPubKey), which can then be used by the assigned name in the DSN.
|
|
||||||
Public keys are used to transmit encrypted data, e.g. for authentication.
|
|
||||||
If the server's public key is known, it should be set manually to avoid expensive and potentially insecure transmissions of the public key from the server to the client each time it is required.
|
|
||||||
|
|
||||||
|
|
||||||
##### `timeout`
|
|
||||||
|
|
||||||
```
|
|
||||||
Type: duration
|
|
||||||
Default: OS default
|
|
||||||
```
|
|
||||||
|
|
||||||
Timeout for establishing connections, aka dial timeout. The value must be a decimal number with a unit suffix (*"ms"*, *"s"*, *"m"*, *"h"*), such as *"30s"*, *"0.5m"* or *"1m30s"*.
|
|
||||||
|
|
||||||
|
|
||||||
##### `tls`
|
|
||||||
|
|
||||||
```
|
|
||||||
Type: bool / string
|
|
||||||
Valid Values: true, false, skip-verify, preferred, <name>
|
|
||||||
Default: false
|
|
||||||
```
|
|
||||||
|
|
||||||
`tls=true` enables TLS / SSL encrypted connection to the server. Use `skip-verify` if you want to use a self-signed or invalid certificate (server side) or use `preferred` to use TLS only when advertised by the server. This is similar to `skip-verify`, but additionally allows a fallback to a connection which is not encrypted. Neither `skip-verify` nor `preferred` add any reliable security. You can use a custom TLS config after registering it with [`mysql.RegisterTLSConfig`](https://godoc.org/github.com/go-sql-driver/mysql#RegisterTLSConfig).
|
|
||||||
|
|
||||||
|
|
||||||
##### `writeTimeout`
|
|
||||||
|
|
||||||
```
|
|
||||||
Type: duration
|
|
||||||
Default: 0
|
|
||||||
```
|
|
||||||
|
|
||||||
I/O write timeout. The value must be a decimal number with a unit suffix (*"ms"*, *"s"*, *"m"*, *"h"*), such as *"30s"*, *"0.5m"* or *"1m30s"*.
|
|
||||||
|
|
||||||
|
|
||||||
##### System Variables
|
|
||||||
|
|
||||||
Any other parameters are interpreted as system variables:
|
|
||||||
* `<boolean_var>=<value>`: `SET <boolean_var>=<value>`
|
|
||||||
* `<enum_var>=<value>`: `SET <enum_var>=<value>`
|
|
||||||
* `<string_var>=%27<value>%27`: `SET <string_var>='<value>'`
|
|
||||||
|
|
||||||
Rules:
|
|
||||||
* The values for string variables must be quoted with `'`.
|
|
||||||
* The values must also be [url.QueryEscape](http://golang.org/pkg/net/url/#QueryEscape)'ed!
|
|
||||||
(which implies values of string variables must be wrapped with `%27`).
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
* `autocommit=1`: `SET autocommit=1`
|
|
||||||
* [`time_zone=%27Europe%2FParis%27`](https://dev.mysql.com/doc/refman/5.5/en/time-zone-support.html): `SET time_zone='Europe/Paris'`
|
|
||||||
* [`transaction_isolation=%27REPEATABLE-READ%27`](https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_transaction_isolation): `SET transaction_isolation='REPEATABLE-READ'`
|
|
||||||
|
|
||||||
|
|
||||||
#### Examples
|
|
||||||
```
|
|
||||||
user@unix(/path/to/socket)/dbname
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
root:pw@unix(/tmp/mysql.sock)/myDatabase?loc=Local
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
user:password@tcp(localhost:5555)/dbname?tls=skip-verify&autocommit=true
|
|
||||||
```
|
|
||||||
|
|
||||||
Treat warnings as errors by setting the system variable [`sql_mode`](https://dev.mysql.com/doc/refman/5.7/en/sql-mode.html):
|
|
||||||
```
|
|
||||||
user:password@/dbname?sql_mode=TRADITIONAL
|
|
||||||
```
|
|
||||||
|
|
||||||
TCP via IPv6:
|
|
||||||
```
|
|
||||||
user:password@tcp([de:ad:be:ef::ca:fe]:80)/dbname?timeout=90s&collation=utf8mb4_unicode_ci
|
|
||||||
```
|
|
||||||
|
|
||||||
TCP on a remote host, e.g. Amazon RDS:
|
|
||||||
```
|
|
||||||
id:password@tcp(your-amazonaws-uri.com:3306)/dbname
|
|
||||||
```
|
|
||||||
|
|
||||||
Google Cloud SQL on App Engine:
|
|
||||||
```
|
|
||||||
user:password@unix(/cloudsql/project-id:region-name:instance-name)/dbname
|
|
||||||
```
|
|
||||||
|
|
||||||
TCP using default port (3306) on localhost:
|
|
||||||
```
|
|
||||||
user:password@tcp/dbname?charset=utf8mb4,utf8&sys_var=esc%40ped
|
|
||||||
```
|
|
||||||
|
|
||||||
Use the default protocol (tcp) and host (localhost:3306):
|
|
||||||
```
|
|
||||||
user:password@/dbname
|
|
||||||
```
|
|
||||||
|
|
||||||
No Database preselected:
|
|
||||||
```
|
|
||||||
user:password@/
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
### Connection pool and timeouts
|
|
||||||
The connection pool is managed by Go's database/sql package. For details on how to configure the size of the pool and how long connections stay in the pool see `*DB.SetMaxOpenConns`, `*DB.SetMaxIdleConns`, and `*DB.SetConnMaxLifetime` in the [database/sql documentation](https://golang.org/pkg/database/sql/). The read, write, and dial timeouts for each individual connection are configured with the DSN parameters [`readTimeout`](#readtimeout), [`writeTimeout`](#writetimeout), and [`timeout`](#timeout), respectively.
|
|
||||||
|
|
||||||
## `ColumnType` Support
|
|
||||||
This driver supports the [`ColumnType` interface](https://golang.org/pkg/database/sql/#ColumnType) introduced in Go 1.8, with the exception of [`ColumnType.Length()`](https://golang.org/pkg/database/sql/#ColumnType.Length), which is currently not supported. All Unsigned database type names will be returned `UNSIGNED ` with `INT`, `TINYINT`, `SMALLINT`, `BIGINT`.
|
|
||||||
|
|
||||||
## `context.Context` Support
|
|
||||||
Go 1.8 added `database/sql` support for `context.Context`. This driver supports query timeouts and cancellation via contexts.
|
|
||||||
See [context support in the database/sql package](https://golang.org/doc/go1.8#database_sql) for more details.
|
|
||||||
|
|
||||||
|
|
||||||
### `LOAD DATA LOCAL INFILE` support
|
|
||||||
For this feature you need direct access to the package. Therefore you must change the import path (no `_`):
|
|
||||||
```go
|
|
||||||
import "github.com/go-sql-driver/mysql"
|
|
||||||
```
|
|
||||||
|
|
||||||
Files must be explicitly allowed by registering them with `mysql.RegisterLocalFile(filepath)` (recommended) or the allowlist check must be deactivated by using the DSN parameter `allowAllFiles=true` ([*Might be insecure!*](http://dev.mysql.com/doc/refman/5.7/en/load-data-local.html)).
|
|
||||||
|
|
||||||
To use a `io.Reader` a handler function must be registered with `mysql.RegisterReaderHandler(name, handler)` which returns a `io.Reader` or `io.ReadCloser`. The Reader is available with the filepath `Reader::<name>` then. Choose different names for different handlers and `DeregisterReaderHandler` when you don't need it anymore.
|
|
||||||
|
|
||||||
See the [godoc of Go-MySQL-Driver](https://godoc.org/github.com/go-sql-driver/mysql "golang mysql driver documentation") for details.
|
|
||||||
|
|
||||||
|
|
||||||
### `time.Time` support
|
|
||||||
The default internal output type of MySQL `DATE` and `DATETIME` values is `[]byte` which allows you to scan the value into a `[]byte`, `string` or `sql.RawBytes` variable in your program.
|
|
||||||
|
|
||||||
However, many want to scan MySQL `DATE` and `DATETIME` values into `time.Time` variables, which is the logical equivalent in Go to `DATE` and `DATETIME` in MySQL. You can do that by changing the internal output type from `[]byte` to `time.Time` with the DSN parameter `parseTime=true`. You can set the default [`time.Time` location](https://golang.org/pkg/time/#Location) with the `loc` DSN parameter.
|
|
||||||
|
|
||||||
**Caution:** As of Go 1.1, this makes `time.Time` the only variable type you can scan `DATE` and `DATETIME` values into. This breaks for example [`sql.RawBytes` support](https://github.com/go-sql-driver/mysql/wiki/Examples#rawbytes).
|
|
||||||
|
|
||||||
|
|
||||||
### Unicode support
|
|
||||||
Since version 1.5 Go-MySQL-Driver automatically uses the collation ` utf8mb4_general_ci` by default.
|
|
||||||
|
|
||||||
Other collations / charsets can be set using the [`collation`](#collation) DSN parameter.
|
|
||||||
|
|
||||||
Version 1.0 of the driver recommended adding `&charset=utf8` (alias for `SET NAMES utf8`) to the DSN to enable proper UTF-8 support. This is not necessary anymore. The [`collation`](#collation) parameter should be preferred to set another collation / charset than the default.
|
|
||||||
|
|
||||||
See http://dev.mysql.com/doc/refman/8.0/en/charset-unicode.html for more details on MySQL's Unicode support.
|
|
||||||
|
|
||||||
## Testing / Development
|
|
||||||
To run the driver tests you may need to adjust the configuration. See the [Testing Wiki-Page](https://github.com/go-sql-driver/mysql/wiki/Testing "Testing") for details.
|
|
||||||
|
|
||||||
Go-MySQL-Driver is not feature-complete yet. Your help is very appreciated.
|
|
||||||
If you want to contribute, you can work on an [open issue](https://github.com/go-sql-driver/mysql/issues?state=open) or review a [pull request](https://github.com/go-sql-driver/mysql/pulls).
|
|
||||||
|
|
||||||
See the [Contribution Guidelines](https://github.com/go-sql-driver/mysql/blob/master/.github/CONTRIBUTING.md) for details.
|
|
||||||
|
|
||||||
---------------------------------------
|
|
||||||
|
|
||||||
## License
|
|
||||||
Go-MySQL-Driver is licensed under the [Mozilla Public License Version 2.0](https://raw.github.com/go-sql-driver/mysql/master/LICENSE)
|
|
||||||
|
|
||||||
Mozilla summarizes the license scope as follows:
|
|
||||||
> MPL: The copyleft applies to any files containing MPLed code.
|
|
||||||
|
|
||||||
|
|
||||||
That means:
|
|
||||||
* You can **use** the **unchanged** source code both in private and commercially.
|
|
||||||
* When distributing, you **must publish** the source code of any **changed files** licensed under the MPL 2.0 under a) the MPL 2.0 itself or b) a compatible license (e.g. GPL 3.0 or Apache License 2.0).
|
|
||||||
* You **needn't publish** the source code of your library as long as the files licensed under the MPL 2.0 are **unchanged**.
|
|
||||||
|
|
||||||
Please read the [MPL 2.0 FAQ](https://www.mozilla.org/en-US/MPL/2.0/FAQ/) if you have further questions regarding the license.
|
|
||||||
|
|
||||||
You can read the full terms here: [LICENSE](https://raw.github.com/go-sql-driver/mysql/master/LICENSE).
|
|
||||||
|
|
||||||

|
|
||||||
19
vendor/github.com/go-sql-driver/mysql/atomic_bool.go
generated
vendored
19
vendor/github.com/go-sql-driver/mysql/atomic_bool.go
generated
vendored
@@ -1,19 +0,0 @@
|
|||||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package.
|
|
||||||
//
|
|
||||||
// Copyright 2022 The Go-MySQL-Driver Authors. All rights reserved.
|
|
||||||
//
|
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
||||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
//go:build go1.19
|
|
||||||
// +build go1.19
|
|
||||||
|
|
||||||
package mysql
|
|
||||||
|
|
||||||
import "sync/atomic"
|
|
||||||
|
|
||||||
/******************************************************************************
|
|
||||||
* Sync utils *
|
|
||||||
******************************************************************************/
|
|
||||||
|
|
||||||
type atomicBool = atomic.Bool
|
|
||||||
47
vendor/github.com/go-sql-driver/mysql/atomic_bool_go118.go
generated
vendored
47
vendor/github.com/go-sql-driver/mysql/atomic_bool_go118.go
generated
vendored
@@ -1,47 +0,0 @@
|
|||||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package.
|
|
||||||
//
|
|
||||||
// Copyright 2022 The Go-MySQL-Driver Authors. All rights reserved.
|
|
||||||
//
|
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
||||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
//go:build !go1.19
|
|
||||||
// +build !go1.19
|
|
||||||
|
|
||||||
package mysql
|
|
||||||
|
|
||||||
import "sync/atomic"
|
|
||||||
|
|
||||||
/******************************************************************************
|
|
||||||
* Sync utils *
|
|
||||||
******************************************************************************/
|
|
||||||
|
|
||||||
// atomicBool is an implementation of atomic.Bool for older version of Go.
|
|
||||||
// it is a wrapper around uint32 for usage as a boolean value with
|
|
||||||
// atomic access.
|
|
||||||
type atomicBool struct {
|
|
||||||
_ noCopy
|
|
||||||
value uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load returns whether the current boolean value is true
|
|
||||||
func (ab *atomicBool) Load() bool {
|
|
||||||
return atomic.LoadUint32(&ab.value) > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store sets the value of the bool regardless of the previous value
|
|
||||||
func (ab *atomicBool) Store(value bool) {
|
|
||||||
if value {
|
|
||||||
atomic.StoreUint32(&ab.value, 1)
|
|
||||||
} else {
|
|
||||||
atomic.StoreUint32(&ab.value, 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Swap sets the value of the bool and returns the old value.
|
|
||||||
func (ab *atomicBool) Swap(value bool) bool {
|
|
||||||
if value {
|
|
||||||
return atomic.SwapUint32(&ab.value, 1) > 0
|
|
||||||
}
|
|
||||||
return atomic.SwapUint32(&ab.value, 0) > 0
|
|
||||||
}
|
|
||||||
437
vendor/github.com/go-sql-driver/mysql/auth.go
generated
vendored
437
vendor/github.com/go-sql-driver/mysql/auth.go
generated
vendored
@@ -1,437 +0,0 @@
|
|||||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
|
||||||
//
|
|
||||||
// Copyright 2018 The Go-MySQL-Driver Authors. All rights reserved.
|
|
||||||
//
|
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
||||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
|
|
||||||
package mysql
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/rsa"
|
|
||||||
"crypto/sha1"
|
|
||||||
"crypto/sha256"
|
|
||||||
"crypto/x509"
|
|
||||||
"encoding/pem"
|
|
||||||
"fmt"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
// server pub keys registry
|
|
||||||
var (
|
|
||||||
serverPubKeyLock sync.RWMutex
|
|
||||||
serverPubKeyRegistry map[string]*rsa.PublicKey
|
|
||||||
)
|
|
||||||
|
|
||||||
// RegisterServerPubKey registers a server RSA public key which can be used to
|
|
||||||
// send data in a secure manner to the server without receiving the public key
|
|
||||||
// in a potentially insecure way from the server first.
|
|
||||||
// Registered keys can afterwards be used adding serverPubKey=<name> to the DSN.
|
|
||||||
//
|
|
||||||
// Note: The provided rsa.PublicKey instance is exclusively owned by the driver
|
|
||||||
// after registering it and may not be modified.
|
|
||||||
//
|
|
||||||
// data, err := ioutil.ReadFile("mykey.pem")
|
|
||||||
// if err != nil {
|
|
||||||
// log.Fatal(err)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// block, _ := pem.Decode(data)
|
|
||||||
// if block == nil || block.Type != "PUBLIC KEY" {
|
|
||||||
// log.Fatal("failed to decode PEM block containing public key")
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// pub, err := x509.ParsePKIXPublicKey(block.Bytes)
|
|
||||||
// if err != nil {
|
|
||||||
// log.Fatal(err)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if rsaPubKey, ok := pub.(*rsa.PublicKey); ok {
|
|
||||||
// mysql.RegisterServerPubKey("mykey", rsaPubKey)
|
|
||||||
// } else {
|
|
||||||
// log.Fatal("not a RSA public key")
|
|
||||||
// }
|
|
||||||
func RegisterServerPubKey(name string, pubKey *rsa.PublicKey) {
|
|
||||||
serverPubKeyLock.Lock()
|
|
||||||
if serverPubKeyRegistry == nil {
|
|
||||||
serverPubKeyRegistry = make(map[string]*rsa.PublicKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
serverPubKeyRegistry[name] = pubKey
|
|
||||||
serverPubKeyLock.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeregisterServerPubKey removes the public key registered with the given name.
|
|
||||||
func DeregisterServerPubKey(name string) {
|
|
||||||
serverPubKeyLock.Lock()
|
|
||||||
if serverPubKeyRegistry != nil {
|
|
||||||
delete(serverPubKeyRegistry, name)
|
|
||||||
}
|
|
||||||
serverPubKeyLock.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func getServerPubKey(name string) (pubKey *rsa.PublicKey) {
|
|
||||||
serverPubKeyLock.RLock()
|
|
||||||
if v, ok := serverPubKeyRegistry[name]; ok {
|
|
||||||
pubKey = v
|
|
||||||
}
|
|
||||||
serverPubKeyLock.RUnlock()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hash password using pre 4.1 (old password) method
|
|
||||||
// https://github.com/atcurtis/mariadb/blob/master/mysys/my_rnd.c
|
|
||||||
type myRnd struct {
|
|
||||||
seed1, seed2 uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
const myRndMaxVal = 0x3FFFFFFF
|
|
||||||
|
|
||||||
// Pseudo random number generator
|
|
||||||
func newMyRnd(seed1, seed2 uint32) *myRnd {
|
|
||||||
return &myRnd{
|
|
||||||
seed1: seed1 % myRndMaxVal,
|
|
||||||
seed2: seed2 % myRndMaxVal,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tested to be equivalent to MariaDB's floating point variant
|
|
||||||
// http://play.golang.org/p/QHvhd4qved
|
|
||||||
// http://play.golang.org/p/RG0q4ElWDx
|
|
||||||
func (r *myRnd) NextByte() byte {
|
|
||||||
r.seed1 = (r.seed1*3 + r.seed2) % myRndMaxVal
|
|
||||||
r.seed2 = (r.seed1 + r.seed2 + 33) % myRndMaxVal
|
|
||||||
|
|
||||||
return byte(uint64(r.seed1) * 31 / myRndMaxVal)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate binary hash from byte string using insecure pre 4.1 method
|
|
||||||
func pwHash(password []byte) (result [2]uint32) {
|
|
||||||
var add uint32 = 7
|
|
||||||
var tmp uint32
|
|
||||||
|
|
||||||
result[0] = 1345345333
|
|
||||||
result[1] = 0x12345671
|
|
||||||
|
|
||||||
for _, c := range password {
|
|
||||||
// skip spaces and tabs in password
|
|
||||||
if c == ' ' || c == '\t' {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
tmp = uint32(c)
|
|
||||||
result[0] ^= (((result[0] & 63) + add) * tmp) + (result[0] << 8)
|
|
||||||
result[1] += (result[1] << 8) ^ result[0]
|
|
||||||
add += tmp
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove sign bit (1<<31)-1)
|
|
||||||
result[0] &= 0x7FFFFFFF
|
|
||||||
result[1] &= 0x7FFFFFFF
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hash password using insecure pre 4.1 method
|
|
||||||
func scrambleOldPassword(scramble []byte, password string) []byte {
|
|
||||||
scramble = scramble[:8]
|
|
||||||
|
|
||||||
hashPw := pwHash([]byte(password))
|
|
||||||
hashSc := pwHash(scramble)
|
|
||||||
|
|
||||||
r := newMyRnd(hashPw[0]^hashSc[0], hashPw[1]^hashSc[1])
|
|
||||||
|
|
||||||
var out [8]byte
|
|
||||||
for i := range out {
|
|
||||||
out[i] = r.NextByte() + 64
|
|
||||||
}
|
|
||||||
|
|
||||||
mask := r.NextByte()
|
|
||||||
for i := range out {
|
|
||||||
out[i] ^= mask
|
|
||||||
}
|
|
||||||
|
|
||||||
return out[:]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hash password using 4.1+ method (SHA1)
|
|
||||||
func scramblePassword(scramble []byte, password string) []byte {
|
|
||||||
if len(password) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// stage1Hash = SHA1(password)
|
|
||||||
crypt := sha1.New()
|
|
||||||
crypt.Write([]byte(password))
|
|
||||||
stage1 := crypt.Sum(nil)
|
|
||||||
|
|
||||||
// scrambleHash = SHA1(scramble + SHA1(stage1Hash))
|
|
||||||
// inner Hash
|
|
||||||
crypt.Reset()
|
|
||||||
crypt.Write(stage1)
|
|
||||||
hash := crypt.Sum(nil)
|
|
||||||
|
|
||||||
// outer Hash
|
|
||||||
crypt.Reset()
|
|
||||||
crypt.Write(scramble)
|
|
||||||
crypt.Write(hash)
|
|
||||||
scramble = crypt.Sum(nil)
|
|
||||||
|
|
||||||
// token = scrambleHash XOR stage1Hash
|
|
||||||
for i := range scramble {
|
|
||||||
scramble[i] ^= stage1[i]
|
|
||||||
}
|
|
||||||
return scramble
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hash password using MySQL 8+ method (SHA256)
|
|
||||||
func scrambleSHA256Password(scramble []byte, password string) []byte {
|
|
||||||
if len(password) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// XOR(SHA256(password), SHA256(SHA256(SHA256(password)), scramble))
|
|
||||||
|
|
||||||
crypt := sha256.New()
|
|
||||||
crypt.Write([]byte(password))
|
|
||||||
message1 := crypt.Sum(nil)
|
|
||||||
|
|
||||||
crypt.Reset()
|
|
||||||
crypt.Write(message1)
|
|
||||||
message1Hash := crypt.Sum(nil)
|
|
||||||
|
|
||||||
crypt.Reset()
|
|
||||||
crypt.Write(message1Hash)
|
|
||||||
crypt.Write(scramble)
|
|
||||||
message2 := crypt.Sum(nil)
|
|
||||||
|
|
||||||
for i := range message1 {
|
|
||||||
message1[i] ^= message2[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
return message1
|
|
||||||
}
|
|
||||||
|
|
||||||
func encryptPassword(password string, seed []byte, pub *rsa.PublicKey) ([]byte, error) {
|
|
||||||
plain := make([]byte, len(password)+1)
|
|
||||||
copy(plain, password)
|
|
||||||
for i := range plain {
|
|
||||||
j := i % len(seed)
|
|
||||||
plain[i] ^= seed[j]
|
|
||||||
}
|
|
||||||
sha1 := sha1.New()
|
|
||||||
return rsa.EncryptOAEP(sha1, rand.Reader, pub, plain, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mc *mysqlConn) sendEncryptedPassword(seed []byte, pub *rsa.PublicKey) error {
|
|
||||||
enc, err := encryptPassword(mc.cfg.Passwd, seed, pub)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return mc.writeAuthSwitchPacket(enc)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mc *mysqlConn) auth(authData []byte, plugin string) ([]byte, error) {
|
|
||||||
switch plugin {
|
|
||||||
case "caching_sha2_password":
|
|
||||||
authResp := scrambleSHA256Password(authData, mc.cfg.Passwd)
|
|
||||||
return authResp, nil
|
|
||||||
|
|
||||||
case "mysql_old_password":
|
|
||||||
if !mc.cfg.AllowOldPasswords {
|
|
||||||
return nil, ErrOldPassword
|
|
||||||
}
|
|
||||||
if len(mc.cfg.Passwd) == 0 {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
// Note: there are edge cases where this should work but doesn't;
|
|
||||||
// this is currently "wontfix":
|
|
||||||
// https://github.com/go-sql-driver/mysql/issues/184
|
|
||||||
authResp := append(scrambleOldPassword(authData[:8], mc.cfg.Passwd), 0)
|
|
||||||
return authResp, nil
|
|
||||||
|
|
||||||
case "mysql_clear_password":
|
|
||||||
if !mc.cfg.AllowCleartextPasswords {
|
|
||||||
return nil, ErrCleartextPassword
|
|
||||||
}
|
|
||||||
// http://dev.mysql.com/doc/refman/5.7/en/cleartext-authentication-plugin.html
|
|
||||||
// http://dev.mysql.com/doc/refman/5.7/en/pam-authentication-plugin.html
|
|
||||||
return append([]byte(mc.cfg.Passwd), 0), nil
|
|
||||||
|
|
||||||
case "mysql_native_password":
|
|
||||||
if !mc.cfg.AllowNativePasswords {
|
|
||||||
return nil, ErrNativePassword
|
|
||||||
}
|
|
||||||
// https://dev.mysql.com/doc/internals/en/secure-password-authentication.html
|
|
||||||
// Native password authentication only need and will need 20-byte challenge.
|
|
||||||
authResp := scramblePassword(authData[:20], mc.cfg.Passwd)
|
|
||||||
return authResp, nil
|
|
||||||
|
|
||||||
case "sha256_password":
|
|
||||||
if len(mc.cfg.Passwd) == 0 {
|
|
||||||
return []byte{0}, nil
|
|
||||||
}
|
|
||||||
// unlike caching_sha2_password, sha256_password does not accept
|
|
||||||
// cleartext password on unix transport.
|
|
||||||
if mc.cfg.TLS != nil {
|
|
||||||
// write cleartext auth packet
|
|
||||||
return append([]byte(mc.cfg.Passwd), 0), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
pubKey := mc.cfg.pubKey
|
|
||||||
if pubKey == nil {
|
|
||||||
// request public key from server
|
|
||||||
return []byte{1}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// encrypted password
|
|
||||||
enc, err := encryptPassword(mc.cfg.Passwd, authData, pubKey)
|
|
||||||
return enc, err
|
|
||||||
|
|
||||||
default:
|
|
||||||
errLog.Print("unknown auth plugin:", plugin)
|
|
||||||
return nil, ErrUnknownPlugin
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mc *mysqlConn) handleAuthResult(oldAuthData []byte, plugin string) error {
|
|
||||||
// Read Result Packet
|
|
||||||
authData, newPlugin, err := mc.readAuthResult()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle auth plugin switch, if requested
|
|
||||||
if newPlugin != "" {
|
|
||||||
// If CLIENT_PLUGIN_AUTH capability is not supported, no new cipher is
|
|
||||||
// sent and we have to keep using the cipher sent in the init packet.
|
|
||||||
if authData == nil {
|
|
||||||
authData = oldAuthData
|
|
||||||
} else {
|
|
||||||
// copy data from read buffer to owned slice
|
|
||||||
copy(oldAuthData, authData)
|
|
||||||
}
|
|
||||||
|
|
||||||
plugin = newPlugin
|
|
||||||
|
|
||||||
authResp, err := mc.auth(authData, plugin)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err = mc.writeAuthSwitchPacket(authResp); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read Result Packet
|
|
||||||
authData, newPlugin, err = mc.readAuthResult()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do not allow to change the auth plugin more than once
|
|
||||||
if newPlugin != "" {
|
|
||||||
return ErrMalformPkt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch plugin {
|
|
||||||
|
|
||||||
// https://insidemysql.com/preparing-your-community-connector-for-mysql-8-part-2-sha256/
|
|
||||||
case "caching_sha2_password":
|
|
||||||
switch len(authData) {
|
|
||||||
case 0:
|
|
||||||
return nil // auth successful
|
|
||||||
case 1:
|
|
||||||
switch authData[0] {
|
|
||||||
case cachingSha2PasswordFastAuthSuccess:
|
|
||||||
if err = mc.readResultOK(); err == nil {
|
|
||||||
return nil // auth successful
|
|
||||||
}
|
|
||||||
|
|
||||||
case cachingSha2PasswordPerformFullAuthentication:
|
|
||||||
if mc.cfg.TLS != nil || mc.cfg.Net == "unix" {
|
|
||||||
// write cleartext auth packet
|
|
||||||
err = mc.writeAuthSwitchPacket(append([]byte(mc.cfg.Passwd), 0))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
pubKey := mc.cfg.pubKey
|
|
||||||
if pubKey == nil {
|
|
||||||
// request public key from server
|
|
||||||
data, err := mc.buf.takeSmallBuffer(4 + 1)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
data[4] = cachingSha2PasswordRequestPublicKey
|
|
||||||
err = mc.writePacket(data)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if data, err = mc.readPacket(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if data[0] != iAuthMoreData {
|
|
||||||
return fmt.Errorf("unexpect resp from server for caching_sha2_password perform full authentication")
|
|
||||||
}
|
|
||||||
|
|
||||||
// parse public key
|
|
||||||
block, rest := pem.Decode(data[1:])
|
|
||||||
if block == nil {
|
|
||||||
return fmt.Errorf("No Pem data found, data: %s", rest)
|
|
||||||
}
|
|
||||||
pkix, err := x509.ParsePKIXPublicKey(block.Bytes)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
pubKey = pkix.(*rsa.PublicKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
// send encrypted password
|
|
||||||
err = mc.sendEncryptedPassword(oldAuthData, pubKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return mc.readResultOK()
|
|
||||||
|
|
||||||
default:
|
|
||||||
return ErrMalformPkt
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return ErrMalformPkt
|
|
||||||
}
|
|
||||||
|
|
||||||
case "sha256_password":
|
|
||||||
switch len(authData) {
|
|
||||||
case 0:
|
|
||||||
return nil // auth successful
|
|
||||||
default:
|
|
||||||
block, _ := pem.Decode(authData)
|
|
||||||
if block == nil {
|
|
||||||
return fmt.Errorf("no Pem data found, data: %s", authData)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub, err := x509.ParsePKIXPublicKey(block.Bytes)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// send encrypted password
|
|
||||||
err = mc.sendEncryptedPassword(oldAuthData, pub.(*rsa.PublicKey))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return mc.readResultOK()
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
return nil // auth successful
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
182
vendor/github.com/go-sql-driver/mysql/buffer.go
generated
vendored
182
vendor/github.com/go-sql-driver/mysql/buffer.go
generated
vendored
@@ -1,182 +0,0 @@
|
|||||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
|
||||||
//
|
|
||||||
// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved.
|
|
||||||
//
|
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
||||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
|
|
||||||
package mysql
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
const defaultBufSize = 4096
|
|
||||||
const maxCachedBufSize = 256 * 1024
|
|
||||||
|
|
||||||
// A buffer which is used for both reading and writing.
|
|
||||||
// This is possible since communication on each connection is synchronous.
|
|
||||||
// In other words, we can't write and read simultaneously on the same connection.
|
|
||||||
// The buffer is similar to bufio.Reader / Writer but zero-copy-ish
|
|
||||||
// Also highly optimized for this particular use case.
|
|
||||||
// This buffer is backed by two byte slices in a double-buffering scheme
|
|
||||||
type buffer struct {
|
|
||||||
buf []byte // buf is a byte buffer who's length and capacity are equal.
|
|
||||||
nc net.Conn
|
|
||||||
idx int
|
|
||||||
length int
|
|
||||||
timeout time.Duration
|
|
||||||
dbuf [2][]byte // dbuf is an array with the two byte slices that back this buffer
|
|
||||||
flipcnt uint // flipccnt is the current buffer counter for double-buffering
|
|
||||||
}
|
|
||||||
|
|
||||||
// newBuffer allocates and returns a new buffer.
|
|
||||||
func newBuffer(nc net.Conn) buffer {
|
|
||||||
fg := make([]byte, defaultBufSize)
|
|
||||||
return buffer{
|
|
||||||
buf: fg,
|
|
||||||
nc: nc,
|
|
||||||
dbuf: [2][]byte{fg, nil},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// flip replaces the active buffer with the background buffer
|
|
||||||
// this is a delayed flip that simply increases the buffer counter;
|
|
||||||
// the actual flip will be performed the next time we call `buffer.fill`
|
|
||||||
func (b *buffer) flip() {
|
|
||||||
b.flipcnt += 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// fill reads into the buffer until at least _need_ bytes are in it
|
|
||||||
func (b *buffer) fill(need int) error {
|
|
||||||
n := b.length
|
|
||||||
// fill data into its double-buffering target: if we've called
|
|
||||||
// flip on this buffer, we'll be copying to the background buffer,
|
|
||||||
// and then filling it with network data; otherwise we'll just move
|
|
||||||
// the contents of the current buffer to the front before filling it
|
|
||||||
dest := b.dbuf[b.flipcnt&1]
|
|
||||||
|
|
||||||
// grow buffer if necessary to fit the whole packet.
|
|
||||||
if need > len(dest) {
|
|
||||||
// Round up to the next multiple of the default size
|
|
||||||
dest = make([]byte, ((need/defaultBufSize)+1)*defaultBufSize)
|
|
||||||
|
|
||||||
// if the allocated buffer is not too large, move it to backing storage
|
|
||||||
// to prevent extra allocations on applications that perform large reads
|
|
||||||
if len(dest) <= maxCachedBufSize {
|
|
||||||
b.dbuf[b.flipcnt&1] = dest
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we're filling the fg buffer, move the existing data to the start of it.
|
|
||||||
// if we're filling the bg buffer, copy over the data
|
|
||||||
if n > 0 {
|
|
||||||
copy(dest[:n], b.buf[b.idx:])
|
|
||||||
}
|
|
||||||
|
|
||||||
b.buf = dest
|
|
||||||
b.idx = 0
|
|
||||||
|
|
||||||
for {
|
|
||||||
if b.timeout > 0 {
|
|
||||||
if err := b.nc.SetReadDeadline(time.Now().Add(b.timeout)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nn, err := b.nc.Read(b.buf[n:])
|
|
||||||
n += nn
|
|
||||||
|
|
||||||
switch err {
|
|
||||||
case nil:
|
|
||||||
if n < need {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
b.length = n
|
|
||||||
return nil
|
|
||||||
|
|
||||||
case io.EOF:
|
|
||||||
if n >= need {
|
|
||||||
b.length = n
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
|
|
||||||
default:
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns next N bytes from buffer.
|
|
||||||
// The returned slice is only guaranteed to be valid until the next read
|
|
||||||
func (b *buffer) readNext(need int) ([]byte, error) {
|
|
||||||
if b.length < need {
|
|
||||||
// refill
|
|
||||||
if err := b.fill(need); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
offset := b.idx
|
|
||||||
b.idx += need
|
|
||||||
b.length -= need
|
|
||||||
return b.buf[offset:b.idx], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// takeBuffer returns a buffer with the requested size.
|
|
||||||
// If possible, a slice from the existing buffer is returned.
|
|
||||||
// Otherwise a bigger buffer is made.
|
|
||||||
// Only one buffer (total) can be used at a time.
|
|
||||||
func (b *buffer) takeBuffer(length int) ([]byte, error) {
|
|
||||||
if b.length > 0 {
|
|
||||||
return nil, ErrBusyBuffer
|
|
||||||
}
|
|
||||||
|
|
||||||
// test (cheap) general case first
|
|
||||||
if length <= cap(b.buf) {
|
|
||||||
return b.buf[:length], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if length < maxPacketSize {
|
|
||||||
b.buf = make([]byte, length)
|
|
||||||
return b.buf, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// buffer is larger than we want to store.
|
|
||||||
return make([]byte, length), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// takeSmallBuffer is shortcut which can be used if length is
|
|
||||||
// known to be smaller than defaultBufSize.
|
|
||||||
// Only one buffer (total) can be used at a time.
|
|
||||||
func (b *buffer) takeSmallBuffer(length int) ([]byte, error) {
|
|
||||||
if b.length > 0 {
|
|
||||||
return nil, ErrBusyBuffer
|
|
||||||
}
|
|
||||||
return b.buf[:length], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// takeCompleteBuffer returns the complete existing buffer.
|
|
||||||
// This can be used if the necessary buffer size is unknown.
|
|
||||||
// cap and len of the returned buffer will be equal.
|
|
||||||
// Only one buffer (total) can be used at a time.
|
|
||||||
func (b *buffer) takeCompleteBuffer() ([]byte, error) {
|
|
||||||
if b.length > 0 {
|
|
||||||
return nil, ErrBusyBuffer
|
|
||||||
}
|
|
||||||
return b.buf, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// store stores buf, an updated buffer, if its suitable to do so.
|
|
||||||
func (b *buffer) store(buf []byte) error {
|
|
||||||
if b.length > 0 {
|
|
||||||
return ErrBusyBuffer
|
|
||||||
} else if cap(buf) <= maxPacketSize && cap(buf) > cap(b.buf) {
|
|
||||||
b.buf = buf[:cap(buf)]
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
266
vendor/github.com/go-sql-driver/mysql/collations.go
generated
vendored
266
vendor/github.com/go-sql-driver/mysql/collations.go
generated
vendored
@@ -1,266 +0,0 @@
|
|||||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
|
||||||
//
|
|
||||||
// Copyright 2014 The Go-MySQL-Driver Authors. All rights reserved.
|
|
||||||
//
|
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
||||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
|
|
||||||
package mysql
|
|
||||||
|
|
||||||
const defaultCollation = "utf8mb4_general_ci"
|
|
||||||
const binaryCollation = "binary"
|
|
||||||
|
|
||||||
// A list of available collations mapped to the internal ID.
|
|
||||||
// To update this map use the following MySQL query:
|
|
||||||
//
|
|
||||||
// SELECT COLLATION_NAME, ID FROM information_schema.COLLATIONS WHERE ID<256 ORDER BY ID
|
|
||||||
//
|
|
||||||
// Handshake packet have only 1 byte for collation_id. So we can't use collations with ID > 255.
|
|
||||||
//
|
|
||||||
// ucs2, utf16, and utf32 can't be used for connection charset.
|
|
||||||
// https://dev.mysql.com/doc/refman/5.7/en/charset-connection.html#charset-connection-impermissible-client-charset
|
|
||||||
// They are commented out to reduce this map.
|
|
||||||
var collations = map[string]byte{
|
|
||||||
"big5_chinese_ci": 1,
|
|
||||||
"latin2_czech_cs": 2,
|
|
||||||
"dec8_swedish_ci": 3,
|
|
||||||
"cp850_general_ci": 4,
|
|
||||||
"latin1_german1_ci": 5,
|
|
||||||
"hp8_english_ci": 6,
|
|
||||||
"koi8r_general_ci": 7,
|
|
||||||
"latin1_swedish_ci": 8,
|
|
||||||
"latin2_general_ci": 9,
|
|
||||||
"swe7_swedish_ci": 10,
|
|
||||||
"ascii_general_ci": 11,
|
|
||||||
"ujis_japanese_ci": 12,
|
|
||||||
"sjis_japanese_ci": 13,
|
|
||||||
"cp1251_bulgarian_ci": 14,
|
|
||||||
"latin1_danish_ci": 15,
|
|
||||||
"hebrew_general_ci": 16,
|
|
||||||
"tis620_thai_ci": 18,
|
|
||||||
"euckr_korean_ci": 19,
|
|
||||||
"latin7_estonian_cs": 20,
|
|
||||||
"latin2_hungarian_ci": 21,
|
|
||||||
"koi8u_general_ci": 22,
|
|
||||||
"cp1251_ukrainian_ci": 23,
|
|
||||||
"gb2312_chinese_ci": 24,
|
|
||||||
"greek_general_ci": 25,
|
|
||||||
"cp1250_general_ci": 26,
|
|
||||||
"latin2_croatian_ci": 27,
|
|
||||||
"gbk_chinese_ci": 28,
|
|
||||||
"cp1257_lithuanian_ci": 29,
|
|
||||||
"latin5_turkish_ci": 30,
|
|
||||||
"latin1_german2_ci": 31,
|
|
||||||
"armscii8_general_ci": 32,
|
|
||||||
"utf8_general_ci": 33,
|
|
||||||
"cp1250_czech_cs": 34,
|
|
||||||
//"ucs2_general_ci": 35,
|
|
||||||
"cp866_general_ci": 36,
|
|
||||||
"keybcs2_general_ci": 37,
|
|
||||||
"macce_general_ci": 38,
|
|
||||||
"macroman_general_ci": 39,
|
|
||||||
"cp852_general_ci": 40,
|
|
||||||
"latin7_general_ci": 41,
|
|
||||||
"latin7_general_cs": 42,
|
|
||||||
"macce_bin": 43,
|
|
||||||
"cp1250_croatian_ci": 44,
|
|
||||||
"utf8mb4_general_ci": 45,
|
|
||||||
"utf8mb4_bin": 46,
|
|
||||||
"latin1_bin": 47,
|
|
||||||
"latin1_general_ci": 48,
|
|
||||||
"latin1_general_cs": 49,
|
|
||||||
"cp1251_bin": 50,
|
|
||||||
"cp1251_general_ci": 51,
|
|
||||||
"cp1251_general_cs": 52,
|
|
||||||
"macroman_bin": 53,
|
|
||||||
//"utf16_general_ci": 54,
|
|
||||||
//"utf16_bin": 55,
|
|
||||||
//"utf16le_general_ci": 56,
|
|
||||||
"cp1256_general_ci": 57,
|
|
||||||
"cp1257_bin": 58,
|
|
||||||
"cp1257_general_ci": 59,
|
|
||||||
//"utf32_general_ci": 60,
|
|
||||||
//"utf32_bin": 61,
|
|
||||||
//"utf16le_bin": 62,
|
|
||||||
"binary": 63,
|
|
||||||
"armscii8_bin": 64,
|
|
||||||
"ascii_bin": 65,
|
|
||||||
"cp1250_bin": 66,
|
|
||||||
"cp1256_bin": 67,
|
|
||||||
"cp866_bin": 68,
|
|
||||||
"dec8_bin": 69,
|
|
||||||
"greek_bin": 70,
|
|
||||||
"hebrew_bin": 71,
|
|
||||||
"hp8_bin": 72,
|
|
||||||
"keybcs2_bin": 73,
|
|
||||||
"koi8r_bin": 74,
|
|
||||||
"koi8u_bin": 75,
|
|
||||||
"utf8_tolower_ci": 76,
|
|
||||||
"latin2_bin": 77,
|
|
||||||
"latin5_bin": 78,
|
|
||||||
"latin7_bin": 79,
|
|
||||||
"cp850_bin": 80,
|
|
||||||
"cp852_bin": 81,
|
|
||||||
"swe7_bin": 82,
|
|
||||||
"utf8_bin": 83,
|
|
||||||
"big5_bin": 84,
|
|
||||||
"euckr_bin": 85,
|
|
||||||
"gb2312_bin": 86,
|
|
||||||
"gbk_bin": 87,
|
|
||||||
"sjis_bin": 88,
|
|
||||||
"tis620_bin": 89,
|
|
||||||
//"ucs2_bin": 90,
|
|
||||||
"ujis_bin": 91,
|
|
||||||
"geostd8_general_ci": 92,
|
|
||||||
"geostd8_bin": 93,
|
|
||||||
"latin1_spanish_ci": 94,
|
|
||||||
"cp932_japanese_ci": 95,
|
|
||||||
"cp932_bin": 96,
|
|
||||||
"eucjpms_japanese_ci": 97,
|
|
||||||
"eucjpms_bin": 98,
|
|
||||||
"cp1250_polish_ci": 99,
|
|
||||||
//"utf16_unicode_ci": 101,
|
|
||||||
//"utf16_icelandic_ci": 102,
|
|
||||||
//"utf16_latvian_ci": 103,
|
|
||||||
//"utf16_romanian_ci": 104,
|
|
||||||
//"utf16_slovenian_ci": 105,
|
|
||||||
//"utf16_polish_ci": 106,
|
|
||||||
//"utf16_estonian_ci": 107,
|
|
||||||
//"utf16_spanish_ci": 108,
|
|
||||||
//"utf16_swedish_ci": 109,
|
|
||||||
//"utf16_turkish_ci": 110,
|
|
||||||
//"utf16_czech_ci": 111,
|
|
||||||
//"utf16_danish_ci": 112,
|
|
||||||
//"utf16_lithuanian_ci": 113,
|
|
||||||
//"utf16_slovak_ci": 114,
|
|
||||||
//"utf16_spanish2_ci": 115,
|
|
||||||
//"utf16_roman_ci": 116,
|
|
||||||
//"utf16_persian_ci": 117,
|
|
||||||
//"utf16_esperanto_ci": 118,
|
|
||||||
//"utf16_hungarian_ci": 119,
|
|
||||||
//"utf16_sinhala_ci": 120,
|
|
||||||
//"utf16_german2_ci": 121,
|
|
||||||
//"utf16_croatian_ci": 122,
|
|
||||||
//"utf16_unicode_520_ci": 123,
|
|
||||||
//"utf16_vietnamese_ci": 124,
|
|
||||||
//"ucs2_unicode_ci": 128,
|
|
||||||
//"ucs2_icelandic_ci": 129,
|
|
||||||
//"ucs2_latvian_ci": 130,
|
|
||||||
//"ucs2_romanian_ci": 131,
|
|
||||||
//"ucs2_slovenian_ci": 132,
|
|
||||||
//"ucs2_polish_ci": 133,
|
|
||||||
//"ucs2_estonian_ci": 134,
|
|
||||||
//"ucs2_spanish_ci": 135,
|
|
||||||
//"ucs2_swedish_ci": 136,
|
|
||||||
//"ucs2_turkish_ci": 137,
|
|
||||||
//"ucs2_czech_ci": 138,
|
|
||||||
//"ucs2_danish_ci": 139,
|
|
||||||
//"ucs2_lithuanian_ci": 140,
|
|
||||||
//"ucs2_slovak_ci": 141,
|
|
||||||
//"ucs2_spanish2_ci": 142,
|
|
||||||
//"ucs2_roman_ci": 143,
|
|
||||||
//"ucs2_persian_ci": 144,
|
|
||||||
//"ucs2_esperanto_ci": 145,
|
|
||||||
//"ucs2_hungarian_ci": 146,
|
|
||||||
//"ucs2_sinhala_ci": 147,
|
|
||||||
//"ucs2_german2_ci": 148,
|
|
||||||
//"ucs2_croatian_ci": 149,
|
|
||||||
//"ucs2_unicode_520_ci": 150,
|
|
||||||
//"ucs2_vietnamese_ci": 151,
|
|
||||||
//"ucs2_general_mysql500_ci": 159,
|
|
||||||
//"utf32_unicode_ci": 160,
|
|
||||||
//"utf32_icelandic_ci": 161,
|
|
||||||
//"utf32_latvian_ci": 162,
|
|
||||||
//"utf32_romanian_ci": 163,
|
|
||||||
//"utf32_slovenian_ci": 164,
|
|
||||||
//"utf32_polish_ci": 165,
|
|
||||||
//"utf32_estonian_ci": 166,
|
|
||||||
//"utf32_spanish_ci": 167,
|
|
||||||
//"utf32_swedish_ci": 168,
|
|
||||||
//"utf32_turkish_ci": 169,
|
|
||||||
//"utf32_czech_ci": 170,
|
|
||||||
//"utf32_danish_ci": 171,
|
|
||||||
//"utf32_lithuanian_ci": 172,
|
|
||||||
//"utf32_slovak_ci": 173,
|
|
||||||
//"utf32_spanish2_ci": 174,
|
|
||||||
//"utf32_roman_ci": 175,
|
|
||||||
//"utf32_persian_ci": 176,
|
|
||||||
//"utf32_esperanto_ci": 177,
|
|
||||||
//"utf32_hungarian_ci": 178,
|
|
||||||
//"utf32_sinhala_ci": 179,
|
|
||||||
//"utf32_german2_ci": 180,
|
|
||||||
//"utf32_croatian_ci": 181,
|
|
||||||
//"utf32_unicode_520_ci": 182,
|
|
||||||
//"utf32_vietnamese_ci": 183,
|
|
||||||
"utf8_unicode_ci": 192,
|
|
||||||
"utf8_icelandic_ci": 193,
|
|
||||||
"utf8_latvian_ci": 194,
|
|
||||||
"utf8_romanian_ci": 195,
|
|
||||||
"utf8_slovenian_ci": 196,
|
|
||||||
"utf8_polish_ci": 197,
|
|
||||||
"utf8_estonian_ci": 198,
|
|
||||||
"utf8_spanish_ci": 199,
|
|
||||||
"utf8_swedish_ci": 200,
|
|
||||||
"utf8_turkish_ci": 201,
|
|
||||||
"utf8_czech_ci": 202,
|
|
||||||
"utf8_danish_ci": 203,
|
|
||||||
"utf8_lithuanian_ci": 204,
|
|
||||||
"utf8_slovak_ci": 205,
|
|
||||||
"utf8_spanish2_ci": 206,
|
|
||||||
"utf8_roman_ci": 207,
|
|
||||||
"utf8_persian_ci": 208,
|
|
||||||
"utf8_esperanto_ci": 209,
|
|
||||||
"utf8_hungarian_ci": 210,
|
|
||||||
"utf8_sinhala_ci": 211,
|
|
||||||
"utf8_german2_ci": 212,
|
|
||||||
"utf8_croatian_ci": 213,
|
|
||||||
"utf8_unicode_520_ci": 214,
|
|
||||||
"utf8_vietnamese_ci": 215,
|
|
||||||
"utf8_general_mysql500_ci": 223,
|
|
||||||
"utf8mb4_unicode_ci": 224,
|
|
||||||
"utf8mb4_icelandic_ci": 225,
|
|
||||||
"utf8mb4_latvian_ci": 226,
|
|
||||||
"utf8mb4_romanian_ci": 227,
|
|
||||||
"utf8mb4_slovenian_ci": 228,
|
|
||||||
"utf8mb4_polish_ci": 229,
|
|
||||||
"utf8mb4_estonian_ci": 230,
|
|
||||||
"utf8mb4_spanish_ci": 231,
|
|
||||||
"utf8mb4_swedish_ci": 232,
|
|
||||||
"utf8mb4_turkish_ci": 233,
|
|
||||||
"utf8mb4_czech_ci": 234,
|
|
||||||
"utf8mb4_danish_ci": 235,
|
|
||||||
"utf8mb4_lithuanian_ci": 236,
|
|
||||||
"utf8mb4_slovak_ci": 237,
|
|
||||||
"utf8mb4_spanish2_ci": 238,
|
|
||||||
"utf8mb4_roman_ci": 239,
|
|
||||||
"utf8mb4_persian_ci": 240,
|
|
||||||
"utf8mb4_esperanto_ci": 241,
|
|
||||||
"utf8mb4_hungarian_ci": 242,
|
|
||||||
"utf8mb4_sinhala_ci": 243,
|
|
||||||
"utf8mb4_german2_ci": 244,
|
|
||||||
"utf8mb4_croatian_ci": 245,
|
|
||||||
"utf8mb4_unicode_520_ci": 246,
|
|
||||||
"utf8mb4_vietnamese_ci": 247,
|
|
||||||
"gb18030_chinese_ci": 248,
|
|
||||||
"gb18030_bin": 249,
|
|
||||||
"gb18030_unicode_520_ci": 250,
|
|
||||||
"utf8mb4_0900_ai_ci": 255,
|
|
||||||
}
|
|
||||||
|
|
||||||
// A denylist of collations which is unsafe to interpolate parameters.
|
|
||||||
// These multibyte encodings may contains 0x5c (`\`) in their trailing bytes.
|
|
||||||
var unsafeCollations = map[string]bool{
|
|
||||||
"big5_chinese_ci": true,
|
|
||||||
"sjis_japanese_ci": true,
|
|
||||||
"gbk_chinese_ci": true,
|
|
||||||
"big5_bin": true,
|
|
||||||
"gb2312_bin": true,
|
|
||||||
"gbk_bin": true,
|
|
||||||
"sjis_bin": true,
|
|
||||||
"cp932_japanese_ci": true,
|
|
||||||
"cp932_bin": true,
|
|
||||||
"gb18030_chinese_ci": true,
|
|
||||||
"gb18030_bin": true,
|
|
||||||
"gb18030_unicode_520_ci": true,
|
|
||||||
}
|
|
||||||
55
vendor/github.com/go-sql-driver/mysql/conncheck.go
generated
vendored
55
vendor/github.com/go-sql-driver/mysql/conncheck.go
generated
vendored
@@ -1,55 +0,0 @@
|
|||||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
|
||||||
//
|
|
||||||
// Copyright 2019 The Go-MySQL-Driver Authors. All rights reserved.
|
|
||||||
//
|
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
||||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
|
|
||||||
//go:build linux || darwin || dragonfly || freebsd || netbsd || openbsd || solaris || illumos
|
|
||||||
// +build linux darwin dragonfly freebsd netbsd openbsd solaris illumos
|
|
||||||
|
|
||||||
package mysql
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
var errUnexpectedRead = errors.New("unexpected read from socket")
|
|
||||||
|
|
||||||
func connCheck(conn net.Conn) error {
|
|
||||||
var sysErr error
|
|
||||||
|
|
||||||
sysConn, ok := conn.(syscall.Conn)
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
rawConn, err := sysConn.SyscallConn()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = rawConn.Read(func(fd uintptr) bool {
|
|
||||||
var buf [1]byte
|
|
||||||
n, err := syscall.Read(int(fd), buf[:])
|
|
||||||
switch {
|
|
||||||
case n == 0 && err == nil:
|
|
||||||
sysErr = io.EOF
|
|
||||||
case n > 0:
|
|
||||||
sysErr = errUnexpectedRead
|
|
||||||
case err == syscall.EAGAIN || err == syscall.EWOULDBLOCK:
|
|
||||||
sysErr = nil
|
|
||||||
default:
|
|
||||||
sysErr = err
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return sysErr
|
|
||||||
}
|
|
||||||
18
vendor/github.com/go-sql-driver/mysql/conncheck_dummy.go
generated
vendored
18
vendor/github.com/go-sql-driver/mysql/conncheck_dummy.go
generated
vendored
@@ -1,18 +0,0 @@
|
|||||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
|
||||||
//
|
|
||||||
// Copyright 2019 The Go-MySQL-Driver Authors. All rights reserved.
|
|
||||||
//
|
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
||||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
|
|
||||||
//go:build !linux && !darwin && !dragonfly && !freebsd && !netbsd && !openbsd && !solaris && !illumos
|
|
||||||
// +build !linux,!darwin,!dragonfly,!freebsd,!netbsd,!openbsd,!solaris,!illumos
|
|
||||||
|
|
||||||
package mysql
|
|
||||||
|
|
||||||
import "net"
|
|
||||||
|
|
||||||
func connCheck(conn net.Conn) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
650
vendor/github.com/go-sql-driver/mysql/connection.go
generated
vendored
650
vendor/github.com/go-sql-driver/mysql/connection.go
generated
vendored
@@ -1,650 +0,0 @@
|
|||||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
|
||||||
//
|
|
||||||
// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
|
|
||||||
//
|
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
||||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
|
|
||||||
package mysql
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"database/sql"
|
|
||||||
"database/sql/driver"
|
|
||||||
"encoding/json"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type mysqlConn struct {
|
|
||||||
buf buffer
|
|
||||||
netConn net.Conn
|
|
||||||
rawConn net.Conn // underlying connection when netConn is TLS connection.
|
|
||||||
affectedRows uint64
|
|
||||||
insertId uint64
|
|
||||||
cfg *Config
|
|
||||||
maxAllowedPacket int
|
|
||||||
maxWriteSize int
|
|
||||||
writeTimeout time.Duration
|
|
||||||
flags clientFlag
|
|
||||||
status statusFlag
|
|
||||||
sequence uint8
|
|
||||||
parseTime bool
|
|
||||||
reset bool // set when the Go SQL package calls ResetSession
|
|
||||||
|
|
||||||
// for context support (Go 1.8+)
|
|
||||||
watching bool
|
|
||||||
watcher chan<- context.Context
|
|
||||||
closech chan struct{}
|
|
||||||
finished chan<- struct{}
|
|
||||||
canceled atomicError // set non-nil if conn is canceled
|
|
||||||
closed atomicBool // set when conn is closed, before closech is closed
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handles parameters set in DSN after the connection is established
|
|
||||||
func (mc *mysqlConn) handleParams() (err error) {
|
|
||||||
var cmdSet strings.Builder
|
|
||||||
for param, val := range mc.cfg.Params {
|
|
||||||
switch param {
|
|
||||||
// Charset: character_set_connection, character_set_client, character_set_results
|
|
||||||
case "charset":
|
|
||||||
charsets := strings.Split(val, ",")
|
|
||||||
for i := range charsets {
|
|
||||||
// ignore errors here - a charset may not exist
|
|
||||||
err = mc.exec("SET NAMES " + charsets[i])
|
|
||||||
if err == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Other system vars accumulated in a single SET command
|
|
||||||
default:
|
|
||||||
if cmdSet.Len() == 0 {
|
|
||||||
// Heuristic: 29 chars for each other key=value to reduce reallocations
|
|
||||||
cmdSet.Grow(4 + len(param) + 1 + len(val) + 30*(len(mc.cfg.Params)-1))
|
|
||||||
cmdSet.WriteString("SET ")
|
|
||||||
} else {
|
|
||||||
cmdSet.WriteString(", ")
|
|
||||||
}
|
|
||||||
cmdSet.WriteString(param)
|
|
||||||
cmdSet.WriteString(" = ")
|
|
||||||
cmdSet.WriteString(val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if cmdSet.Len() > 0 {
|
|
||||||
err = mc.exec(cmdSet.String())
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mc *mysqlConn) markBadConn(err error) error {
|
|
||||||
if mc == nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err != errBadConnNoWrite {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return driver.ErrBadConn
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mc *mysqlConn) Begin() (driver.Tx, error) {
|
|
||||||
return mc.begin(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mc *mysqlConn) begin(readOnly bool) (driver.Tx, error) {
|
|
||||||
if mc.closed.Load() {
|
|
||||||
errLog.Print(ErrInvalidConn)
|
|
||||||
return nil, driver.ErrBadConn
|
|
||||||
}
|
|
||||||
var q string
|
|
||||||
if readOnly {
|
|
||||||
q = "START TRANSACTION READ ONLY"
|
|
||||||
} else {
|
|
||||||
q = "START TRANSACTION"
|
|
||||||
}
|
|
||||||
err := mc.exec(q)
|
|
||||||
if err == nil {
|
|
||||||
return &mysqlTx{mc}, err
|
|
||||||
}
|
|
||||||
return nil, mc.markBadConn(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mc *mysqlConn) Close() (err error) {
|
|
||||||
// Makes Close idempotent
|
|
||||||
if !mc.closed.Load() {
|
|
||||||
err = mc.writeCommandPacket(comQuit)
|
|
||||||
}
|
|
||||||
|
|
||||||
mc.cleanup()
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Closes the network connection and unsets internal variables. Do not call this
|
|
||||||
// function after successfully authentication, call Close instead. This function
|
|
||||||
// is called before auth or on auth failure because MySQL will have already
|
|
||||||
// closed the network connection.
|
|
||||||
func (mc *mysqlConn) cleanup() {
|
|
||||||
if mc.closed.Swap(true) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Makes cleanup idempotent
|
|
||||||
close(mc.closech)
|
|
||||||
if mc.netConn == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err := mc.netConn.Close(); err != nil {
|
|
||||||
errLog.Print(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mc *mysqlConn) error() error {
|
|
||||||
if mc.closed.Load() {
|
|
||||||
if err := mc.canceled.Value(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return ErrInvalidConn
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mc *mysqlConn) Prepare(query string) (driver.Stmt, error) {
|
|
||||||
if mc.closed.Load() {
|
|
||||||
errLog.Print(ErrInvalidConn)
|
|
||||||
return nil, driver.ErrBadConn
|
|
||||||
}
|
|
||||||
// Send command
|
|
||||||
err := mc.writeCommandPacketStr(comStmtPrepare, query)
|
|
||||||
if err != nil {
|
|
||||||
// STMT_PREPARE is safe to retry. So we can return ErrBadConn here.
|
|
||||||
errLog.Print(err)
|
|
||||||
return nil, driver.ErrBadConn
|
|
||||||
}
|
|
||||||
|
|
||||||
stmt := &mysqlStmt{
|
|
||||||
mc: mc,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read Result
|
|
||||||
columnCount, err := stmt.readPrepareResultPacket()
|
|
||||||
if err == nil {
|
|
||||||
if stmt.paramCount > 0 {
|
|
||||||
if err = mc.readUntilEOF(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if columnCount > 0 {
|
|
||||||
err = mc.readUntilEOF()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return stmt, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mc *mysqlConn) interpolateParams(query string, args []driver.Value) (string, error) {
|
|
||||||
// Number of ? should be same to len(args)
|
|
||||||
if strings.Count(query, "?") != len(args) {
|
|
||||||
return "", driver.ErrSkip
|
|
||||||
}
|
|
||||||
|
|
||||||
buf, err := mc.buf.takeCompleteBuffer()
|
|
||||||
if err != nil {
|
|
||||||
// can not take the buffer. Something must be wrong with the connection
|
|
||||||
errLog.Print(err)
|
|
||||||
return "", ErrInvalidConn
|
|
||||||
}
|
|
||||||
buf = buf[:0]
|
|
||||||
argPos := 0
|
|
||||||
|
|
||||||
for i := 0; i < len(query); i++ {
|
|
||||||
q := strings.IndexByte(query[i:], '?')
|
|
||||||
if q == -1 {
|
|
||||||
buf = append(buf, query[i:]...)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
buf = append(buf, query[i:i+q]...)
|
|
||||||
i += q
|
|
||||||
|
|
||||||
arg := args[argPos]
|
|
||||||
argPos++
|
|
||||||
|
|
||||||
if arg == nil {
|
|
||||||
buf = append(buf, "NULL"...)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
switch v := arg.(type) {
|
|
||||||
case int64:
|
|
||||||
buf = strconv.AppendInt(buf, v, 10)
|
|
||||||
case uint64:
|
|
||||||
// Handle uint64 explicitly because our custom ConvertValue emits unsigned values
|
|
||||||
buf = strconv.AppendUint(buf, v, 10)
|
|
||||||
case float64:
|
|
||||||
buf = strconv.AppendFloat(buf, v, 'g', -1, 64)
|
|
||||||
case bool:
|
|
||||||
if v {
|
|
||||||
buf = append(buf, '1')
|
|
||||||
} else {
|
|
||||||
buf = append(buf, '0')
|
|
||||||
}
|
|
||||||
case time.Time:
|
|
||||||
if v.IsZero() {
|
|
||||||
buf = append(buf, "'0000-00-00'"...)
|
|
||||||
} else {
|
|
||||||
buf = append(buf, '\'')
|
|
||||||
buf, err = appendDateTime(buf, v.In(mc.cfg.Loc))
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
buf = append(buf, '\'')
|
|
||||||
}
|
|
||||||
case json.RawMessage:
|
|
||||||
buf = append(buf, '\'')
|
|
||||||
if mc.status&statusNoBackslashEscapes == 0 {
|
|
||||||
buf = escapeBytesBackslash(buf, v)
|
|
||||||
} else {
|
|
||||||
buf = escapeBytesQuotes(buf, v)
|
|
||||||
}
|
|
||||||
buf = append(buf, '\'')
|
|
||||||
case []byte:
|
|
||||||
if v == nil {
|
|
||||||
buf = append(buf, "NULL"...)
|
|
||||||
} else {
|
|
||||||
buf = append(buf, "_binary'"...)
|
|
||||||
if mc.status&statusNoBackslashEscapes == 0 {
|
|
||||||
buf = escapeBytesBackslash(buf, v)
|
|
||||||
} else {
|
|
||||||
buf = escapeBytesQuotes(buf, v)
|
|
||||||
}
|
|
||||||
buf = append(buf, '\'')
|
|
||||||
}
|
|
||||||
case string:
|
|
||||||
buf = append(buf, '\'')
|
|
||||||
if mc.status&statusNoBackslashEscapes == 0 {
|
|
||||||
buf = escapeStringBackslash(buf, v)
|
|
||||||
} else {
|
|
||||||
buf = escapeStringQuotes(buf, v)
|
|
||||||
}
|
|
||||||
buf = append(buf, '\'')
|
|
||||||
default:
|
|
||||||
return "", driver.ErrSkip
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(buf)+4 > mc.maxAllowedPacket {
|
|
||||||
return "", driver.ErrSkip
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if argPos != len(args) {
|
|
||||||
return "", driver.ErrSkip
|
|
||||||
}
|
|
||||||
return string(buf), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mc *mysqlConn) Exec(query string, args []driver.Value) (driver.Result, error) {
|
|
||||||
if mc.closed.Load() {
|
|
||||||
errLog.Print(ErrInvalidConn)
|
|
||||||
return nil, driver.ErrBadConn
|
|
||||||
}
|
|
||||||
if len(args) != 0 {
|
|
||||||
if !mc.cfg.InterpolateParams {
|
|
||||||
return nil, driver.ErrSkip
|
|
||||||
}
|
|
||||||
// try to interpolate the parameters to save extra roundtrips for preparing and closing a statement
|
|
||||||
prepared, err := mc.interpolateParams(query, args)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
query = prepared
|
|
||||||
}
|
|
||||||
mc.affectedRows = 0
|
|
||||||
mc.insertId = 0
|
|
||||||
|
|
||||||
err := mc.exec(query)
|
|
||||||
if err == nil {
|
|
||||||
return &mysqlResult{
|
|
||||||
affectedRows: int64(mc.affectedRows),
|
|
||||||
insertId: int64(mc.insertId),
|
|
||||||
}, err
|
|
||||||
}
|
|
||||||
return nil, mc.markBadConn(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Internal function to execute commands
|
|
||||||
func (mc *mysqlConn) exec(query string) error {
|
|
||||||
// Send command
|
|
||||||
if err := mc.writeCommandPacketStr(comQuery, query); err != nil {
|
|
||||||
return mc.markBadConn(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read Result
|
|
||||||
resLen, err := mc.readResultSetHeaderPacket()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if resLen > 0 {
|
|
||||||
// columns
|
|
||||||
if err := mc.readUntilEOF(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// rows
|
|
||||||
if err := mc.readUntilEOF(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return mc.discardResults()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mc *mysqlConn) Query(query string, args []driver.Value) (driver.Rows, error) {
|
|
||||||
return mc.query(query, args)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mc *mysqlConn) query(query string, args []driver.Value) (*textRows, error) {
|
|
||||||
if mc.closed.Load() {
|
|
||||||
errLog.Print(ErrInvalidConn)
|
|
||||||
return nil, driver.ErrBadConn
|
|
||||||
}
|
|
||||||
if len(args) != 0 {
|
|
||||||
if !mc.cfg.InterpolateParams {
|
|
||||||
return nil, driver.ErrSkip
|
|
||||||
}
|
|
||||||
// try client-side prepare to reduce roundtrip
|
|
||||||
prepared, err := mc.interpolateParams(query, args)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
query = prepared
|
|
||||||
}
|
|
||||||
// Send command
|
|
||||||
err := mc.writeCommandPacketStr(comQuery, query)
|
|
||||||
if err == nil {
|
|
||||||
// Read Result
|
|
||||||
var resLen int
|
|
||||||
resLen, err = mc.readResultSetHeaderPacket()
|
|
||||||
if err == nil {
|
|
||||||
rows := new(textRows)
|
|
||||||
rows.mc = mc
|
|
||||||
|
|
||||||
if resLen == 0 {
|
|
||||||
rows.rs.done = true
|
|
||||||
|
|
||||||
switch err := rows.NextResultSet(); err {
|
|
||||||
case nil, io.EOF:
|
|
||||||
return rows, nil
|
|
||||||
default:
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Columns
|
|
||||||
rows.rs.columns, err = mc.readColumns(resLen)
|
|
||||||
return rows, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, mc.markBadConn(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gets the value of the given MySQL System Variable
|
|
||||||
// The returned byte slice is only valid until the next read
|
|
||||||
func (mc *mysqlConn) getSystemVar(name string) ([]byte, error) {
|
|
||||||
// Send command
|
|
||||||
if err := mc.writeCommandPacketStr(comQuery, "SELECT @@"+name); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read Result
|
|
||||||
resLen, err := mc.readResultSetHeaderPacket()
|
|
||||||
if err == nil {
|
|
||||||
rows := new(textRows)
|
|
||||||
rows.mc = mc
|
|
||||||
rows.rs.columns = []mysqlField{{fieldType: fieldTypeVarChar}}
|
|
||||||
|
|
||||||
if resLen > 0 {
|
|
||||||
// Columns
|
|
||||||
if err := mc.readUntilEOF(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dest := make([]driver.Value, resLen)
|
|
||||||
if err = rows.readRow(dest); err == nil {
|
|
||||||
return dest[0].([]byte), mc.readUntilEOF()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// finish is called when the query has canceled.
|
|
||||||
func (mc *mysqlConn) cancel(err error) {
|
|
||||||
mc.canceled.Set(err)
|
|
||||||
mc.cleanup()
|
|
||||||
}
|
|
||||||
|
|
||||||
// finish is called when the query has succeeded.
|
|
||||||
func (mc *mysqlConn) finish() {
|
|
||||||
if !mc.watching || mc.finished == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
select {
|
|
||||||
case mc.finished <- struct{}{}:
|
|
||||||
mc.watching = false
|
|
||||||
case <-mc.closech:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ping implements driver.Pinger interface
|
|
||||||
func (mc *mysqlConn) Ping(ctx context.Context) (err error) {
|
|
||||||
if mc.closed.Load() {
|
|
||||||
errLog.Print(ErrInvalidConn)
|
|
||||||
return driver.ErrBadConn
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = mc.watchCancel(ctx); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer mc.finish()
|
|
||||||
|
|
||||||
if err = mc.writeCommandPacket(comPing); err != nil {
|
|
||||||
return mc.markBadConn(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return mc.readResultOK()
|
|
||||||
}
|
|
||||||
|
|
||||||
// BeginTx implements driver.ConnBeginTx interface
|
|
||||||
func (mc *mysqlConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) {
|
|
||||||
if mc.closed.Load() {
|
|
||||||
return nil, driver.ErrBadConn
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := mc.watchCancel(ctx); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer mc.finish()
|
|
||||||
|
|
||||||
if sql.IsolationLevel(opts.Isolation) != sql.LevelDefault {
|
|
||||||
level, err := mapIsolationLevel(opts.Isolation)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
err = mc.exec("SET TRANSACTION ISOLATION LEVEL " + level)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return mc.begin(opts.ReadOnly)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mc *mysqlConn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) {
|
|
||||||
dargs, err := namedValueToValue(args)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := mc.watchCancel(ctx); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
rows, err := mc.query(query, dargs)
|
|
||||||
if err != nil {
|
|
||||||
mc.finish()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
rows.finish = mc.finish
|
|
||||||
return rows, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mc *mysqlConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) {
|
|
||||||
dargs, err := namedValueToValue(args)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := mc.watchCancel(ctx); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer mc.finish()
|
|
||||||
|
|
||||||
return mc.Exec(query, dargs)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mc *mysqlConn) PrepareContext(ctx context.Context, query string) (driver.Stmt, error) {
|
|
||||||
if err := mc.watchCancel(ctx); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
stmt, err := mc.Prepare(query)
|
|
||||||
mc.finish()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
default:
|
|
||||||
case <-ctx.Done():
|
|
||||||
stmt.Close()
|
|
||||||
return nil, ctx.Err()
|
|
||||||
}
|
|
||||||
return stmt, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (stmt *mysqlStmt) QueryContext(ctx context.Context, args []driver.NamedValue) (driver.Rows, error) {
|
|
||||||
dargs, err := namedValueToValue(args)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := stmt.mc.watchCancel(ctx); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
rows, err := stmt.query(dargs)
|
|
||||||
if err != nil {
|
|
||||||
stmt.mc.finish()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
rows.finish = stmt.mc.finish
|
|
||||||
return rows, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (stmt *mysqlStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (driver.Result, error) {
|
|
||||||
dargs, err := namedValueToValue(args)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := stmt.mc.watchCancel(ctx); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer stmt.mc.finish()
|
|
||||||
|
|
||||||
return stmt.Exec(dargs)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mc *mysqlConn) watchCancel(ctx context.Context) error {
|
|
||||||
if mc.watching {
|
|
||||||
// Reach here if canceled,
|
|
||||||
// so the connection is already invalid
|
|
||||||
mc.cleanup()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// When ctx is already cancelled, don't watch it.
|
|
||||||
if err := ctx.Err(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// When ctx is not cancellable, don't watch it.
|
|
||||||
if ctx.Done() == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// When watcher is not alive, can't watch it.
|
|
||||||
if mc.watcher == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
mc.watching = true
|
|
||||||
mc.watcher <- ctx
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mc *mysqlConn) startWatcher() {
|
|
||||||
watcher := make(chan context.Context, 1)
|
|
||||||
mc.watcher = watcher
|
|
||||||
finished := make(chan struct{})
|
|
||||||
mc.finished = finished
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
var ctx context.Context
|
|
||||||
select {
|
|
||||||
case ctx = <-watcher:
|
|
||||||
case <-mc.closech:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-ctx.Done():
|
|
||||||
mc.cancel(ctx.Err())
|
|
||||||
case <-finished:
|
|
||||||
case <-mc.closech:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mc *mysqlConn) CheckNamedValue(nv *driver.NamedValue) (err error) {
|
|
||||||
nv.Value, err = converter{}.ConvertValue(nv.Value)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// ResetSession implements driver.SessionResetter.
|
|
||||||
// (From Go 1.10)
|
|
||||||
func (mc *mysqlConn) ResetSession(ctx context.Context) error {
|
|
||||||
if mc.closed.Load() {
|
|
||||||
return driver.ErrBadConn
|
|
||||||
}
|
|
||||||
mc.reset = true
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsValid implements driver.Validator interface
|
|
||||||
// (From Go 1.15)
|
|
||||||
func (mc *mysqlConn) IsValid() bool {
|
|
||||||
return !mc.closed.Load()
|
|
||||||
}
|
|
||||||
146
vendor/github.com/go-sql-driver/mysql/connector.go
generated
vendored
146
vendor/github.com/go-sql-driver/mysql/connector.go
generated
vendored
@@ -1,146 +0,0 @@
|
|||||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
|
||||||
//
|
|
||||||
// Copyright 2018 The Go-MySQL-Driver Authors. All rights reserved.
|
|
||||||
//
|
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
||||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
|
|
||||||
package mysql
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"database/sql/driver"
|
|
||||||
"net"
|
|
||||||
)
|
|
||||||
|
|
||||||
type connector struct {
|
|
||||||
cfg *Config // immutable private copy.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Connect implements driver.Connector interface.
|
|
||||||
// Connect returns a connection to the database.
|
|
||||||
func (c *connector) Connect(ctx context.Context) (driver.Conn, error) {
|
|
||||||
var err error
|
|
||||||
|
|
||||||
// New mysqlConn
|
|
||||||
mc := &mysqlConn{
|
|
||||||
maxAllowedPacket: maxPacketSize,
|
|
||||||
maxWriteSize: maxPacketSize - 1,
|
|
||||||
closech: make(chan struct{}),
|
|
||||||
cfg: c.cfg,
|
|
||||||
}
|
|
||||||
mc.parseTime = mc.cfg.ParseTime
|
|
||||||
|
|
||||||
// Connect to Server
|
|
||||||
dialsLock.RLock()
|
|
||||||
dial, ok := dials[mc.cfg.Net]
|
|
||||||
dialsLock.RUnlock()
|
|
||||||
if ok {
|
|
||||||
dctx := ctx
|
|
||||||
if mc.cfg.Timeout > 0 {
|
|
||||||
var cancel context.CancelFunc
|
|
||||||
dctx, cancel = context.WithTimeout(ctx, c.cfg.Timeout)
|
|
||||||
defer cancel()
|
|
||||||
}
|
|
||||||
mc.netConn, err = dial(dctx, mc.cfg.Addr)
|
|
||||||
} else {
|
|
||||||
nd := net.Dialer{Timeout: mc.cfg.Timeout}
|
|
||||||
mc.netConn, err = nd.DialContext(ctx, mc.cfg.Net, mc.cfg.Addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enable TCP Keepalives on TCP connections
|
|
||||||
if tc, ok := mc.netConn.(*net.TCPConn); ok {
|
|
||||||
if err := tc.SetKeepAlive(true); err != nil {
|
|
||||||
// Don't send COM_QUIT before handshake.
|
|
||||||
mc.netConn.Close()
|
|
||||||
mc.netConn = nil
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call startWatcher for context support (From Go 1.8)
|
|
||||||
mc.startWatcher()
|
|
||||||
if err := mc.watchCancel(ctx); err != nil {
|
|
||||||
mc.cleanup()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
defer mc.finish()
|
|
||||||
|
|
||||||
mc.buf = newBuffer(mc.netConn)
|
|
||||||
|
|
||||||
// Set I/O timeouts
|
|
||||||
mc.buf.timeout = mc.cfg.ReadTimeout
|
|
||||||
mc.writeTimeout = mc.cfg.WriteTimeout
|
|
||||||
|
|
||||||
// Reading Handshake Initialization Packet
|
|
||||||
authData, plugin, err := mc.readHandshakePacket()
|
|
||||||
if err != nil {
|
|
||||||
mc.cleanup()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if plugin == "" {
|
|
||||||
plugin = defaultAuthPlugin
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send Client Authentication Packet
|
|
||||||
authResp, err := mc.auth(authData, plugin)
|
|
||||||
if err != nil {
|
|
||||||
// try the default auth plugin, if using the requested plugin failed
|
|
||||||
errLog.Print("could not use requested auth plugin '"+plugin+"': ", err.Error())
|
|
||||||
plugin = defaultAuthPlugin
|
|
||||||
authResp, err = mc.auth(authData, plugin)
|
|
||||||
if err != nil {
|
|
||||||
mc.cleanup()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err = mc.writeHandshakeResponsePacket(authResp, plugin); err != nil {
|
|
||||||
mc.cleanup()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle response to auth packet, switch methods if possible
|
|
||||||
if err = mc.handleAuthResult(authData, plugin); err != nil {
|
|
||||||
// Authentication failed and MySQL has already closed the connection
|
|
||||||
// (https://dev.mysql.com/doc/internals/en/authentication-fails.html).
|
|
||||||
// Do not send COM_QUIT, just cleanup and return the error.
|
|
||||||
mc.cleanup()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if mc.cfg.MaxAllowedPacket > 0 {
|
|
||||||
mc.maxAllowedPacket = mc.cfg.MaxAllowedPacket
|
|
||||||
} else {
|
|
||||||
// Get max allowed packet size
|
|
||||||
maxap, err := mc.getSystemVar("max_allowed_packet")
|
|
||||||
if err != nil {
|
|
||||||
mc.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
mc.maxAllowedPacket = stringToInt(maxap) - 1
|
|
||||||
}
|
|
||||||
if mc.maxAllowedPacket < maxPacketSize {
|
|
||||||
mc.maxWriteSize = mc.maxAllowedPacket
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle DSN Params
|
|
||||||
err = mc.handleParams()
|
|
||||||
if err != nil {
|
|
||||||
mc.Close()
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return mc, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Driver implements driver.Connector interface.
|
|
||||||
// Driver returns &MySQLDriver{}.
|
|
||||||
func (c *connector) Driver() driver.Driver {
|
|
||||||
return &MySQLDriver{}
|
|
||||||
}
|
|
||||||
174
vendor/github.com/go-sql-driver/mysql/const.go
generated
vendored
174
vendor/github.com/go-sql-driver/mysql/const.go
generated
vendored
@@ -1,174 +0,0 @@
|
|||||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
|
||||||
//
|
|
||||||
// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
|
|
||||||
//
|
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
||||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
|
|
||||||
package mysql
|
|
||||||
|
|
||||||
const (
|
|
||||||
defaultAuthPlugin = "mysql_native_password"
|
|
||||||
defaultMaxAllowedPacket = 64 << 20 // 64 MiB. See https://github.com/go-sql-driver/mysql/issues/1355
|
|
||||||
minProtocolVersion = 10
|
|
||||||
maxPacketSize = 1<<24 - 1
|
|
||||||
timeFormat = "2006-01-02 15:04:05.999999"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MySQL constants documentation:
|
|
||||||
// http://dev.mysql.com/doc/internals/en/client-server-protocol.html
|
|
||||||
|
|
||||||
const (
|
|
||||||
iOK byte = 0x00
|
|
||||||
iAuthMoreData byte = 0x01
|
|
||||||
iLocalInFile byte = 0xfb
|
|
||||||
iEOF byte = 0xfe
|
|
||||||
iERR byte = 0xff
|
|
||||||
)
|
|
||||||
|
|
||||||
// https://dev.mysql.com/doc/internals/en/capability-flags.html#packet-Protocol::CapabilityFlags
|
|
||||||
type clientFlag uint32
|
|
||||||
|
|
||||||
const (
|
|
||||||
clientLongPassword clientFlag = 1 << iota
|
|
||||||
clientFoundRows
|
|
||||||
clientLongFlag
|
|
||||||
clientConnectWithDB
|
|
||||||
clientNoSchema
|
|
||||||
clientCompress
|
|
||||||
clientODBC
|
|
||||||
clientLocalFiles
|
|
||||||
clientIgnoreSpace
|
|
||||||
clientProtocol41
|
|
||||||
clientInteractive
|
|
||||||
clientSSL
|
|
||||||
clientIgnoreSIGPIPE
|
|
||||||
clientTransactions
|
|
||||||
clientReserved
|
|
||||||
clientSecureConn
|
|
||||||
clientMultiStatements
|
|
||||||
clientMultiResults
|
|
||||||
clientPSMultiResults
|
|
||||||
clientPluginAuth
|
|
||||||
clientConnectAttrs
|
|
||||||
clientPluginAuthLenEncClientData
|
|
||||||
clientCanHandleExpiredPasswords
|
|
||||||
clientSessionTrack
|
|
||||||
clientDeprecateEOF
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
comQuit byte = iota + 1
|
|
||||||
comInitDB
|
|
||||||
comQuery
|
|
||||||
comFieldList
|
|
||||||
comCreateDB
|
|
||||||
comDropDB
|
|
||||||
comRefresh
|
|
||||||
comShutdown
|
|
||||||
comStatistics
|
|
||||||
comProcessInfo
|
|
||||||
comConnect
|
|
||||||
comProcessKill
|
|
||||||
comDebug
|
|
||||||
comPing
|
|
||||||
comTime
|
|
||||||
comDelayedInsert
|
|
||||||
comChangeUser
|
|
||||||
comBinlogDump
|
|
||||||
comTableDump
|
|
||||||
comConnectOut
|
|
||||||
comRegisterSlave
|
|
||||||
comStmtPrepare
|
|
||||||
comStmtExecute
|
|
||||||
comStmtSendLongData
|
|
||||||
comStmtClose
|
|
||||||
comStmtReset
|
|
||||||
comSetOption
|
|
||||||
comStmtFetch
|
|
||||||
)
|
|
||||||
|
|
||||||
// https://dev.mysql.com/doc/internals/en/com-query-response.html#packet-Protocol::ColumnType
|
|
||||||
type fieldType byte
|
|
||||||
|
|
||||||
const (
|
|
||||||
fieldTypeDecimal fieldType = iota
|
|
||||||
fieldTypeTiny
|
|
||||||
fieldTypeShort
|
|
||||||
fieldTypeLong
|
|
||||||
fieldTypeFloat
|
|
||||||
fieldTypeDouble
|
|
||||||
fieldTypeNULL
|
|
||||||
fieldTypeTimestamp
|
|
||||||
fieldTypeLongLong
|
|
||||||
fieldTypeInt24
|
|
||||||
fieldTypeDate
|
|
||||||
fieldTypeTime
|
|
||||||
fieldTypeDateTime
|
|
||||||
fieldTypeYear
|
|
||||||
fieldTypeNewDate
|
|
||||||
fieldTypeVarChar
|
|
||||||
fieldTypeBit
|
|
||||||
)
|
|
||||||
const (
|
|
||||||
fieldTypeJSON fieldType = iota + 0xf5
|
|
||||||
fieldTypeNewDecimal
|
|
||||||
fieldTypeEnum
|
|
||||||
fieldTypeSet
|
|
||||||
fieldTypeTinyBLOB
|
|
||||||
fieldTypeMediumBLOB
|
|
||||||
fieldTypeLongBLOB
|
|
||||||
fieldTypeBLOB
|
|
||||||
fieldTypeVarString
|
|
||||||
fieldTypeString
|
|
||||||
fieldTypeGeometry
|
|
||||||
)
|
|
||||||
|
|
||||||
type fieldFlag uint16
|
|
||||||
|
|
||||||
const (
|
|
||||||
flagNotNULL fieldFlag = 1 << iota
|
|
||||||
flagPriKey
|
|
||||||
flagUniqueKey
|
|
||||||
flagMultipleKey
|
|
||||||
flagBLOB
|
|
||||||
flagUnsigned
|
|
||||||
flagZeroFill
|
|
||||||
flagBinary
|
|
||||||
flagEnum
|
|
||||||
flagAutoIncrement
|
|
||||||
flagTimestamp
|
|
||||||
flagSet
|
|
||||||
flagUnknown1
|
|
||||||
flagUnknown2
|
|
||||||
flagUnknown3
|
|
||||||
flagUnknown4
|
|
||||||
)
|
|
||||||
|
|
||||||
// http://dev.mysql.com/doc/internals/en/status-flags.html
|
|
||||||
type statusFlag uint16
|
|
||||||
|
|
||||||
const (
|
|
||||||
statusInTrans statusFlag = 1 << iota
|
|
||||||
statusInAutocommit
|
|
||||||
statusReserved // Not in documentation
|
|
||||||
statusMoreResultsExists
|
|
||||||
statusNoGoodIndexUsed
|
|
||||||
statusNoIndexUsed
|
|
||||||
statusCursorExists
|
|
||||||
statusLastRowSent
|
|
||||||
statusDbDropped
|
|
||||||
statusNoBackslashEscapes
|
|
||||||
statusMetadataChanged
|
|
||||||
statusQueryWasSlow
|
|
||||||
statusPsOutParams
|
|
||||||
statusInTransReadonly
|
|
||||||
statusSessionStateChanged
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
cachingSha2PasswordRequestPublicKey = 2
|
|
||||||
cachingSha2PasswordFastAuthSuccess = 3
|
|
||||||
cachingSha2PasswordPerformFullAuthentication = 4
|
|
||||||
)
|
|
||||||
107
vendor/github.com/go-sql-driver/mysql/driver.go
generated
vendored
107
vendor/github.com/go-sql-driver/mysql/driver.go
generated
vendored
@@ -1,107 +0,0 @@
|
|||||||
// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
|
|
||||||
//
|
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
||||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
|
|
||||||
// Package mysql provides a MySQL driver for Go's database/sql package.
|
|
||||||
//
|
|
||||||
// The driver should be used via the database/sql package:
|
|
||||||
//
|
|
||||||
// import "database/sql"
|
|
||||||
// import _ "github.com/go-sql-driver/mysql"
|
|
||||||
//
|
|
||||||
// db, err := sql.Open("mysql", "user:password@/dbname")
|
|
||||||
//
|
|
||||||
// See https://github.com/go-sql-driver/mysql#usage for details
|
|
||||||
package mysql
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"database/sql"
|
|
||||||
"database/sql/driver"
|
|
||||||
"net"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
// MySQLDriver is exported to make the driver directly accessible.
|
|
||||||
// In general the driver is used via the database/sql package.
|
|
||||||
type MySQLDriver struct{}
|
|
||||||
|
|
||||||
// DialFunc is a function which can be used to establish the network connection.
|
|
||||||
// Custom dial functions must be registered with RegisterDial
|
|
||||||
//
|
|
||||||
// Deprecated: users should register a DialContextFunc instead
|
|
||||||
type DialFunc func(addr string) (net.Conn, error)
|
|
||||||
|
|
||||||
// DialContextFunc is a function which can be used to establish the network connection.
|
|
||||||
// Custom dial functions must be registered with RegisterDialContext
|
|
||||||
type DialContextFunc func(ctx context.Context, addr string) (net.Conn, error)
|
|
||||||
|
|
||||||
var (
|
|
||||||
dialsLock sync.RWMutex
|
|
||||||
dials map[string]DialContextFunc
|
|
||||||
)
|
|
||||||
|
|
||||||
// RegisterDialContext registers a custom dial function. It can then be used by the
|
|
||||||
// network address mynet(addr), where mynet is the registered new network.
|
|
||||||
// The current context for the connection and its address is passed to the dial function.
|
|
||||||
func RegisterDialContext(net string, dial DialContextFunc) {
|
|
||||||
dialsLock.Lock()
|
|
||||||
defer dialsLock.Unlock()
|
|
||||||
if dials == nil {
|
|
||||||
dials = make(map[string]DialContextFunc)
|
|
||||||
}
|
|
||||||
dials[net] = dial
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterDial registers a custom dial function. It can then be used by the
|
|
||||||
// network address mynet(addr), where mynet is the registered new network.
|
|
||||||
// addr is passed as a parameter to the dial function.
|
|
||||||
//
|
|
||||||
// Deprecated: users should call RegisterDialContext instead
|
|
||||||
func RegisterDial(network string, dial DialFunc) {
|
|
||||||
RegisterDialContext(network, func(_ context.Context, addr string) (net.Conn, error) {
|
|
||||||
return dial(addr)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open new Connection.
|
|
||||||
// See https://github.com/go-sql-driver/mysql#dsn-data-source-name for how
|
|
||||||
// the DSN string is formatted
|
|
||||||
func (d MySQLDriver) Open(dsn string) (driver.Conn, error) {
|
|
||||||
cfg, err := ParseDSN(dsn)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
c := &connector{
|
|
||||||
cfg: cfg,
|
|
||||||
}
|
|
||||||
return c.Connect(context.Background())
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
sql.Register("mysql", &MySQLDriver{})
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewConnector returns new driver.Connector.
|
|
||||||
func NewConnector(cfg *Config) (driver.Connector, error) {
|
|
||||||
cfg = cfg.Clone()
|
|
||||||
// normalize the contents of cfg so calls to NewConnector have the same
|
|
||||||
// behavior as MySQLDriver.OpenConnector
|
|
||||||
if err := cfg.normalize(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &connector{cfg: cfg}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// OpenConnector implements driver.DriverContext.
|
|
||||||
func (d MySQLDriver) OpenConnector(dsn string) (driver.Connector, error) {
|
|
||||||
cfg, err := ParseDSN(dsn)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &connector{
|
|
||||||
cfg: cfg,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
577
vendor/github.com/go-sql-driver/mysql/dsn.go
generated
vendored
577
vendor/github.com/go-sql-driver/mysql/dsn.go
generated
vendored
@@ -1,577 +0,0 @@
|
|||||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
|
||||||
//
|
|
||||||
// Copyright 2016 The Go-MySQL-Driver Authors. All rights reserved.
|
|
||||||
//
|
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
||||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
|
|
||||||
package mysql
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/rsa"
|
|
||||||
"crypto/tls"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"math/big"
|
|
||||||
"net"
|
|
||||||
"net/url"
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
errInvalidDSNUnescaped = errors.New("invalid DSN: did you forget to escape a param value?")
|
|
||||||
errInvalidDSNAddr = errors.New("invalid DSN: network address not terminated (missing closing brace)")
|
|
||||||
errInvalidDSNNoSlash = errors.New("invalid DSN: missing the slash separating the database name")
|
|
||||||
errInvalidDSNUnsafeCollation = errors.New("invalid DSN: interpolateParams can not be used with unsafe collations")
|
|
||||||
)
|
|
||||||
|
|
||||||
// Config is a configuration parsed from a DSN string.
|
|
||||||
// If a new Config is created instead of being parsed from a DSN string,
|
|
||||||
// the NewConfig function should be used, which sets default values.
|
|
||||||
type Config struct {
|
|
||||||
User string // Username
|
|
||||||
Passwd string // Password (requires User)
|
|
||||||
Net string // Network type
|
|
||||||
Addr string // Network address (requires Net)
|
|
||||||
DBName string // Database name
|
|
||||||
Params map[string]string // Connection parameters
|
|
||||||
Collation string // Connection collation
|
|
||||||
Loc *time.Location // Location for time.Time values
|
|
||||||
MaxAllowedPacket int // Max packet size allowed
|
|
||||||
ServerPubKey string // Server public key name
|
|
||||||
pubKey *rsa.PublicKey // Server public key
|
|
||||||
TLSConfig string // TLS configuration name
|
|
||||||
TLS *tls.Config // TLS configuration, its priority is higher than TLSConfig
|
|
||||||
Timeout time.Duration // Dial timeout
|
|
||||||
ReadTimeout time.Duration // I/O read timeout
|
|
||||||
WriteTimeout time.Duration // I/O write timeout
|
|
||||||
|
|
||||||
AllowAllFiles bool // Allow all files to be used with LOAD DATA LOCAL INFILE
|
|
||||||
AllowCleartextPasswords bool // Allows the cleartext client side plugin
|
|
||||||
AllowFallbackToPlaintext bool // Allows fallback to unencrypted connection if server does not support TLS
|
|
||||||
AllowNativePasswords bool // Allows the native password authentication method
|
|
||||||
AllowOldPasswords bool // Allows the old insecure password method
|
|
||||||
CheckConnLiveness bool // Check connections for liveness before using them
|
|
||||||
ClientFoundRows bool // Return number of matching rows instead of rows changed
|
|
||||||
ColumnsWithAlias bool // Prepend table alias to column names
|
|
||||||
InterpolateParams bool // Interpolate placeholders into query string
|
|
||||||
MultiStatements bool // Allow multiple statements in one query
|
|
||||||
ParseTime bool // Parse time values to time.Time
|
|
||||||
RejectReadOnly bool // Reject read-only connections
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewConfig creates a new Config and sets default values.
|
|
||||||
func NewConfig() *Config {
|
|
||||||
return &Config{
|
|
||||||
Collation: defaultCollation,
|
|
||||||
Loc: time.UTC,
|
|
||||||
MaxAllowedPacket: defaultMaxAllowedPacket,
|
|
||||||
AllowNativePasswords: true,
|
|
||||||
CheckConnLiveness: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *Config) Clone() *Config {
|
|
||||||
cp := *cfg
|
|
||||||
if cp.TLS != nil {
|
|
||||||
cp.TLS = cfg.TLS.Clone()
|
|
||||||
}
|
|
||||||
if len(cp.Params) > 0 {
|
|
||||||
cp.Params = make(map[string]string, len(cfg.Params))
|
|
||||||
for k, v := range cfg.Params {
|
|
||||||
cp.Params[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if cfg.pubKey != nil {
|
|
||||||
cp.pubKey = &rsa.PublicKey{
|
|
||||||
N: new(big.Int).Set(cfg.pubKey.N),
|
|
||||||
E: cfg.pubKey.E,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &cp
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *Config) normalize() error {
|
|
||||||
if cfg.InterpolateParams && unsafeCollations[cfg.Collation] {
|
|
||||||
return errInvalidDSNUnsafeCollation
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set default network if empty
|
|
||||||
if cfg.Net == "" {
|
|
||||||
cfg.Net = "tcp"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set default address if empty
|
|
||||||
if cfg.Addr == "" {
|
|
||||||
switch cfg.Net {
|
|
||||||
case "tcp":
|
|
||||||
cfg.Addr = "127.0.0.1:3306"
|
|
||||||
case "unix":
|
|
||||||
cfg.Addr = "/tmp/mysql.sock"
|
|
||||||
default:
|
|
||||||
return errors.New("default addr for network '" + cfg.Net + "' unknown")
|
|
||||||
}
|
|
||||||
} else if cfg.Net == "tcp" {
|
|
||||||
cfg.Addr = ensureHavePort(cfg.Addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.TLS == nil {
|
|
||||||
switch cfg.TLSConfig {
|
|
||||||
case "false", "":
|
|
||||||
// don't set anything
|
|
||||||
case "true":
|
|
||||||
cfg.TLS = &tls.Config{}
|
|
||||||
case "skip-verify":
|
|
||||||
cfg.TLS = &tls.Config{InsecureSkipVerify: true}
|
|
||||||
case "preferred":
|
|
||||||
cfg.TLS = &tls.Config{InsecureSkipVerify: true}
|
|
||||||
cfg.AllowFallbackToPlaintext = true
|
|
||||||
default:
|
|
||||||
cfg.TLS = getTLSConfigClone(cfg.TLSConfig)
|
|
||||||
if cfg.TLS == nil {
|
|
||||||
return errors.New("invalid value / unknown config name: " + cfg.TLSConfig)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.TLS != nil && cfg.TLS.ServerName == "" && !cfg.TLS.InsecureSkipVerify {
|
|
||||||
host, _, err := net.SplitHostPort(cfg.Addr)
|
|
||||||
if err == nil {
|
|
||||||
cfg.TLS.ServerName = host
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.ServerPubKey != "" {
|
|
||||||
cfg.pubKey = getServerPubKey(cfg.ServerPubKey)
|
|
||||||
if cfg.pubKey == nil {
|
|
||||||
return errors.New("invalid value / unknown server pub key name: " + cfg.ServerPubKey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func writeDSNParam(buf *bytes.Buffer, hasParam *bool, name, value string) {
|
|
||||||
buf.Grow(1 + len(name) + 1 + len(value))
|
|
||||||
if !*hasParam {
|
|
||||||
*hasParam = true
|
|
||||||
buf.WriteByte('?')
|
|
||||||
} else {
|
|
||||||
buf.WriteByte('&')
|
|
||||||
}
|
|
||||||
buf.WriteString(name)
|
|
||||||
buf.WriteByte('=')
|
|
||||||
buf.WriteString(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FormatDSN formats the given Config into a DSN string which can be passed to
|
|
||||||
// the driver.
|
|
||||||
func (cfg *Config) FormatDSN() string {
|
|
||||||
var buf bytes.Buffer
|
|
||||||
|
|
||||||
// [username[:password]@]
|
|
||||||
if len(cfg.User) > 0 {
|
|
||||||
buf.WriteString(cfg.User)
|
|
||||||
if len(cfg.Passwd) > 0 {
|
|
||||||
buf.WriteByte(':')
|
|
||||||
buf.WriteString(cfg.Passwd)
|
|
||||||
}
|
|
||||||
buf.WriteByte('@')
|
|
||||||
}
|
|
||||||
|
|
||||||
// [protocol[(address)]]
|
|
||||||
if len(cfg.Net) > 0 {
|
|
||||||
buf.WriteString(cfg.Net)
|
|
||||||
if len(cfg.Addr) > 0 {
|
|
||||||
buf.WriteByte('(')
|
|
||||||
buf.WriteString(cfg.Addr)
|
|
||||||
buf.WriteByte(')')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// /dbname
|
|
||||||
buf.WriteByte('/')
|
|
||||||
buf.WriteString(cfg.DBName)
|
|
||||||
|
|
||||||
// [?param1=value1&...¶mN=valueN]
|
|
||||||
hasParam := false
|
|
||||||
|
|
||||||
if cfg.AllowAllFiles {
|
|
||||||
hasParam = true
|
|
||||||
buf.WriteString("?allowAllFiles=true")
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.AllowCleartextPasswords {
|
|
||||||
writeDSNParam(&buf, &hasParam, "allowCleartextPasswords", "true")
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.AllowFallbackToPlaintext {
|
|
||||||
writeDSNParam(&buf, &hasParam, "allowFallbackToPlaintext", "true")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !cfg.AllowNativePasswords {
|
|
||||||
writeDSNParam(&buf, &hasParam, "allowNativePasswords", "false")
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.AllowOldPasswords {
|
|
||||||
writeDSNParam(&buf, &hasParam, "allowOldPasswords", "true")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !cfg.CheckConnLiveness {
|
|
||||||
writeDSNParam(&buf, &hasParam, "checkConnLiveness", "false")
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.ClientFoundRows {
|
|
||||||
writeDSNParam(&buf, &hasParam, "clientFoundRows", "true")
|
|
||||||
}
|
|
||||||
|
|
||||||
if col := cfg.Collation; col != defaultCollation && len(col) > 0 {
|
|
||||||
writeDSNParam(&buf, &hasParam, "collation", col)
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.ColumnsWithAlias {
|
|
||||||
writeDSNParam(&buf, &hasParam, "columnsWithAlias", "true")
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.InterpolateParams {
|
|
||||||
writeDSNParam(&buf, &hasParam, "interpolateParams", "true")
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.Loc != time.UTC && cfg.Loc != nil {
|
|
||||||
writeDSNParam(&buf, &hasParam, "loc", url.QueryEscape(cfg.Loc.String()))
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.MultiStatements {
|
|
||||||
writeDSNParam(&buf, &hasParam, "multiStatements", "true")
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.ParseTime {
|
|
||||||
writeDSNParam(&buf, &hasParam, "parseTime", "true")
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.ReadTimeout > 0 {
|
|
||||||
writeDSNParam(&buf, &hasParam, "readTimeout", cfg.ReadTimeout.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.RejectReadOnly {
|
|
||||||
writeDSNParam(&buf, &hasParam, "rejectReadOnly", "true")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(cfg.ServerPubKey) > 0 {
|
|
||||||
writeDSNParam(&buf, &hasParam, "serverPubKey", url.QueryEscape(cfg.ServerPubKey))
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.Timeout > 0 {
|
|
||||||
writeDSNParam(&buf, &hasParam, "timeout", cfg.Timeout.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(cfg.TLSConfig) > 0 {
|
|
||||||
writeDSNParam(&buf, &hasParam, "tls", url.QueryEscape(cfg.TLSConfig))
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.WriteTimeout > 0 {
|
|
||||||
writeDSNParam(&buf, &hasParam, "writeTimeout", cfg.WriteTimeout.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.MaxAllowedPacket != defaultMaxAllowedPacket {
|
|
||||||
writeDSNParam(&buf, &hasParam, "maxAllowedPacket", strconv.Itoa(cfg.MaxAllowedPacket))
|
|
||||||
}
|
|
||||||
|
|
||||||
// other params
|
|
||||||
if cfg.Params != nil {
|
|
||||||
var params []string
|
|
||||||
for param := range cfg.Params {
|
|
||||||
params = append(params, param)
|
|
||||||
}
|
|
||||||
sort.Strings(params)
|
|
||||||
for _, param := range params {
|
|
||||||
writeDSNParam(&buf, &hasParam, param, url.QueryEscape(cfg.Params[param]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParseDSN parses the DSN string to a Config
|
|
||||||
func ParseDSN(dsn string) (cfg *Config, err error) {
|
|
||||||
// New config with some default values
|
|
||||||
cfg = NewConfig()
|
|
||||||
|
|
||||||
// [user[:password]@][net[(addr)]]/dbname[?param1=value1¶mN=valueN]
|
|
||||||
// Find the last '/' (since the password or the net addr might contain a '/')
|
|
||||||
foundSlash := false
|
|
||||||
for i := len(dsn) - 1; i >= 0; i-- {
|
|
||||||
if dsn[i] == '/' {
|
|
||||||
foundSlash = true
|
|
||||||
var j, k int
|
|
||||||
|
|
||||||
// left part is empty if i <= 0
|
|
||||||
if i > 0 {
|
|
||||||
// [username[:password]@][protocol[(address)]]
|
|
||||||
// Find the last '@' in dsn[:i]
|
|
||||||
for j = i; j >= 0; j-- {
|
|
||||||
if dsn[j] == '@' {
|
|
||||||
// username[:password]
|
|
||||||
// Find the first ':' in dsn[:j]
|
|
||||||
for k = 0; k < j; k++ {
|
|
||||||
if dsn[k] == ':' {
|
|
||||||
cfg.Passwd = dsn[k+1 : j]
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cfg.User = dsn[:k]
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// [protocol[(address)]]
|
|
||||||
// Find the first '(' in dsn[j+1:i]
|
|
||||||
for k = j + 1; k < i; k++ {
|
|
||||||
if dsn[k] == '(' {
|
|
||||||
// dsn[i-1] must be == ')' if an address is specified
|
|
||||||
if dsn[i-1] != ')' {
|
|
||||||
if strings.ContainsRune(dsn[k+1:i], ')') {
|
|
||||||
return nil, errInvalidDSNUnescaped
|
|
||||||
}
|
|
||||||
return nil, errInvalidDSNAddr
|
|
||||||
}
|
|
||||||
cfg.Addr = dsn[k+1 : i-1]
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cfg.Net = dsn[j+1 : k]
|
|
||||||
}
|
|
||||||
|
|
||||||
// dbname[?param1=value1&...¶mN=valueN]
|
|
||||||
// Find the first '?' in dsn[i+1:]
|
|
||||||
for j = i + 1; j < len(dsn); j++ {
|
|
||||||
if dsn[j] == '?' {
|
|
||||||
if err = parseDSNParams(cfg, dsn[j+1:]); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cfg.DBName = dsn[i+1 : j]
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !foundSlash && len(dsn) > 0 {
|
|
||||||
return nil, errInvalidDSNNoSlash
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = cfg.normalize(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseDSNParams parses the DSN "query string"
|
|
||||||
// Values must be url.QueryEscape'ed
|
|
||||||
func parseDSNParams(cfg *Config, params string) (err error) {
|
|
||||||
for _, v := range strings.Split(params, "&") {
|
|
||||||
param := strings.SplitN(v, "=", 2)
|
|
||||||
if len(param) != 2 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// cfg params
|
|
||||||
switch value := param[1]; param[0] {
|
|
||||||
// Disable INFILE allowlist / enable all files
|
|
||||||
case "allowAllFiles":
|
|
||||||
var isBool bool
|
|
||||||
cfg.AllowAllFiles, isBool = readBool(value)
|
|
||||||
if !isBool {
|
|
||||||
return errors.New("invalid bool value: " + value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use cleartext authentication mode (MySQL 5.5.10+)
|
|
||||||
case "allowCleartextPasswords":
|
|
||||||
var isBool bool
|
|
||||||
cfg.AllowCleartextPasswords, isBool = readBool(value)
|
|
||||||
if !isBool {
|
|
||||||
return errors.New("invalid bool value: " + value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allow fallback to unencrypted connection if server does not support TLS
|
|
||||||
case "allowFallbackToPlaintext":
|
|
||||||
var isBool bool
|
|
||||||
cfg.AllowFallbackToPlaintext, isBool = readBool(value)
|
|
||||||
if !isBool {
|
|
||||||
return errors.New("invalid bool value: " + value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use native password authentication
|
|
||||||
case "allowNativePasswords":
|
|
||||||
var isBool bool
|
|
||||||
cfg.AllowNativePasswords, isBool = readBool(value)
|
|
||||||
if !isBool {
|
|
||||||
return errors.New("invalid bool value: " + value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use old authentication mode (pre MySQL 4.1)
|
|
||||||
case "allowOldPasswords":
|
|
||||||
var isBool bool
|
|
||||||
cfg.AllowOldPasswords, isBool = readBool(value)
|
|
||||||
if !isBool {
|
|
||||||
return errors.New("invalid bool value: " + value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check connections for Liveness before using them
|
|
||||||
case "checkConnLiveness":
|
|
||||||
var isBool bool
|
|
||||||
cfg.CheckConnLiveness, isBool = readBool(value)
|
|
||||||
if !isBool {
|
|
||||||
return errors.New("invalid bool value: " + value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Switch "rowsAffected" mode
|
|
||||||
case "clientFoundRows":
|
|
||||||
var isBool bool
|
|
||||||
cfg.ClientFoundRows, isBool = readBool(value)
|
|
||||||
if !isBool {
|
|
||||||
return errors.New("invalid bool value: " + value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collation
|
|
||||||
case "collation":
|
|
||||||
cfg.Collation = value
|
|
||||||
|
|
||||||
case "columnsWithAlias":
|
|
||||||
var isBool bool
|
|
||||||
cfg.ColumnsWithAlias, isBool = readBool(value)
|
|
||||||
if !isBool {
|
|
||||||
return errors.New("invalid bool value: " + value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compression
|
|
||||||
case "compress":
|
|
||||||
return errors.New("compression not implemented yet")
|
|
||||||
|
|
||||||
// Enable client side placeholder substitution
|
|
||||||
case "interpolateParams":
|
|
||||||
var isBool bool
|
|
||||||
cfg.InterpolateParams, isBool = readBool(value)
|
|
||||||
if !isBool {
|
|
||||||
return errors.New("invalid bool value: " + value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Time Location
|
|
||||||
case "loc":
|
|
||||||
if value, err = url.QueryUnescape(value); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
cfg.Loc, err = time.LoadLocation(value)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// multiple statements in one query
|
|
||||||
case "multiStatements":
|
|
||||||
var isBool bool
|
|
||||||
cfg.MultiStatements, isBool = readBool(value)
|
|
||||||
if !isBool {
|
|
||||||
return errors.New("invalid bool value: " + value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// time.Time parsing
|
|
||||||
case "parseTime":
|
|
||||||
var isBool bool
|
|
||||||
cfg.ParseTime, isBool = readBool(value)
|
|
||||||
if !isBool {
|
|
||||||
return errors.New("invalid bool value: " + value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// I/O read Timeout
|
|
||||||
case "readTimeout":
|
|
||||||
cfg.ReadTimeout, err = time.ParseDuration(value)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reject read-only connections
|
|
||||||
case "rejectReadOnly":
|
|
||||||
var isBool bool
|
|
||||||
cfg.RejectReadOnly, isBool = readBool(value)
|
|
||||||
if !isBool {
|
|
||||||
return errors.New("invalid bool value: " + value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Server public key
|
|
||||||
case "serverPubKey":
|
|
||||||
name, err := url.QueryUnescape(value)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("invalid value for server pub key name: %v", err)
|
|
||||||
}
|
|
||||||
cfg.ServerPubKey = name
|
|
||||||
|
|
||||||
// Strict mode
|
|
||||||
case "strict":
|
|
||||||
panic("strict mode has been removed. See https://github.com/go-sql-driver/mysql/wiki/strict-mode")
|
|
||||||
|
|
||||||
// Dial Timeout
|
|
||||||
case "timeout":
|
|
||||||
cfg.Timeout, err = time.ParseDuration(value)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// TLS-Encryption
|
|
||||||
case "tls":
|
|
||||||
boolValue, isBool := readBool(value)
|
|
||||||
if isBool {
|
|
||||||
if boolValue {
|
|
||||||
cfg.TLSConfig = "true"
|
|
||||||
} else {
|
|
||||||
cfg.TLSConfig = "false"
|
|
||||||
}
|
|
||||||
} else if vl := strings.ToLower(value); vl == "skip-verify" || vl == "preferred" {
|
|
||||||
cfg.TLSConfig = vl
|
|
||||||
} else {
|
|
||||||
name, err := url.QueryUnescape(value)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("invalid value for TLS config name: %v", err)
|
|
||||||
}
|
|
||||||
cfg.TLSConfig = name
|
|
||||||
}
|
|
||||||
|
|
||||||
// I/O write Timeout
|
|
||||||
case "writeTimeout":
|
|
||||||
cfg.WriteTimeout, err = time.ParseDuration(value)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
case "maxAllowedPacket":
|
|
||||||
cfg.MaxAllowedPacket, err = strconv.Atoi(value)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
// lazy init
|
|
||||||
if cfg.Params == nil {
|
|
||||||
cfg.Params = make(map[string]string)
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.Params[param[0]], err = url.QueryUnescape(value); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func ensureHavePort(addr string) string {
|
|
||||||
if _, _, err := net.SplitHostPort(addr); err != nil {
|
|
||||||
return net.JoinHostPort(addr, "3306")
|
|
||||||
}
|
|
||||||
return addr
|
|
||||||
}
|
|
||||||
77
vendor/github.com/go-sql-driver/mysql/errors.go
generated
vendored
77
vendor/github.com/go-sql-driver/mysql/errors.go
generated
vendored
@@ -1,77 +0,0 @@
|
|||||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
|
||||||
//
|
|
||||||
// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved.
|
|
||||||
//
|
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
||||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
|
|
||||||
package mysql
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Various errors the driver might return. Can change between driver versions.
|
|
||||||
var (
|
|
||||||
ErrInvalidConn = errors.New("invalid connection")
|
|
||||||
ErrMalformPkt = errors.New("malformed packet")
|
|
||||||
ErrNoTLS = errors.New("TLS requested but server does not support TLS")
|
|
||||||
ErrCleartextPassword = errors.New("this user requires clear text authentication. If you still want to use it, please add 'allowCleartextPasswords=1' to your DSN")
|
|
||||||
ErrNativePassword = errors.New("this user requires mysql native password authentication.")
|
|
||||||
ErrOldPassword = errors.New("this user requires old password authentication. If you still want to use it, please add 'allowOldPasswords=1' to your DSN. See also https://github.com/go-sql-driver/mysql/wiki/old_passwords")
|
|
||||||
ErrUnknownPlugin = errors.New("this authentication plugin is not supported")
|
|
||||||
ErrOldProtocol = errors.New("MySQL server does not support required protocol 41+")
|
|
||||||
ErrPktSync = errors.New("commands out of sync. You can't run this command now")
|
|
||||||
ErrPktSyncMul = errors.New("commands out of sync. Did you run multiple statements at once?")
|
|
||||||
ErrPktTooLarge = errors.New("packet for query is too large. Try adjusting the `Config.MaxAllowedPacket`")
|
|
||||||
ErrBusyBuffer = errors.New("busy buffer")
|
|
||||||
|
|
||||||
// errBadConnNoWrite is used for connection errors where nothing was sent to the database yet.
|
|
||||||
// If this happens first in a function starting a database interaction, it should be replaced by driver.ErrBadConn
|
|
||||||
// to trigger a resend.
|
|
||||||
// See https://github.com/go-sql-driver/mysql/pull/302
|
|
||||||
errBadConnNoWrite = errors.New("bad connection")
|
|
||||||
)
|
|
||||||
|
|
||||||
var errLog = Logger(log.New(os.Stderr, "[mysql] ", log.Ldate|log.Ltime|log.Lshortfile))
|
|
||||||
|
|
||||||
// Logger is used to log critical error messages.
|
|
||||||
type Logger interface {
|
|
||||||
Print(v ...interface{})
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetLogger is used to set the logger for critical errors.
|
|
||||||
// The initial logger is os.Stderr.
|
|
||||||
func SetLogger(logger Logger) error {
|
|
||||||
if logger == nil {
|
|
||||||
return errors.New("logger is nil")
|
|
||||||
}
|
|
||||||
errLog = logger
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// MySQLError is an error type which represents a single MySQL error
|
|
||||||
type MySQLError struct {
|
|
||||||
Number uint16
|
|
||||||
SQLState [5]byte
|
|
||||||
Message string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (me *MySQLError) Error() string {
|
|
||||||
if me.SQLState != [5]byte{} {
|
|
||||||
return fmt.Sprintf("Error %d (%s): %s", me.Number, me.SQLState, me.Message)
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Sprintf("Error %d: %s", me.Number, me.Message)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (me *MySQLError) Is(err error) bool {
|
|
||||||
if merr, ok := err.(*MySQLError); ok {
|
|
||||||
return merr.Number == me.Number
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
206
vendor/github.com/go-sql-driver/mysql/fields.go
generated
vendored
206
vendor/github.com/go-sql-driver/mysql/fields.go
generated
vendored
@@ -1,206 +0,0 @@
|
|||||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
|
||||||
//
|
|
||||||
// Copyright 2017 The Go-MySQL-Driver Authors. All rights reserved.
|
|
||||||
//
|
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
||||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
|
|
||||||
package mysql
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
"reflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (mf *mysqlField) typeDatabaseName() string {
|
|
||||||
switch mf.fieldType {
|
|
||||||
case fieldTypeBit:
|
|
||||||
return "BIT"
|
|
||||||
case fieldTypeBLOB:
|
|
||||||
if mf.charSet != collations[binaryCollation] {
|
|
||||||
return "TEXT"
|
|
||||||
}
|
|
||||||
return "BLOB"
|
|
||||||
case fieldTypeDate:
|
|
||||||
return "DATE"
|
|
||||||
case fieldTypeDateTime:
|
|
||||||
return "DATETIME"
|
|
||||||
case fieldTypeDecimal:
|
|
||||||
return "DECIMAL"
|
|
||||||
case fieldTypeDouble:
|
|
||||||
return "DOUBLE"
|
|
||||||
case fieldTypeEnum:
|
|
||||||
return "ENUM"
|
|
||||||
case fieldTypeFloat:
|
|
||||||
return "FLOAT"
|
|
||||||
case fieldTypeGeometry:
|
|
||||||
return "GEOMETRY"
|
|
||||||
case fieldTypeInt24:
|
|
||||||
return "MEDIUMINT"
|
|
||||||
case fieldTypeJSON:
|
|
||||||
return "JSON"
|
|
||||||
case fieldTypeLong:
|
|
||||||
if mf.flags&flagUnsigned != 0 {
|
|
||||||
return "UNSIGNED INT"
|
|
||||||
}
|
|
||||||
return "INT"
|
|
||||||
case fieldTypeLongBLOB:
|
|
||||||
if mf.charSet != collations[binaryCollation] {
|
|
||||||
return "LONGTEXT"
|
|
||||||
}
|
|
||||||
return "LONGBLOB"
|
|
||||||
case fieldTypeLongLong:
|
|
||||||
if mf.flags&flagUnsigned != 0 {
|
|
||||||
return "UNSIGNED BIGINT"
|
|
||||||
}
|
|
||||||
return "BIGINT"
|
|
||||||
case fieldTypeMediumBLOB:
|
|
||||||
if mf.charSet != collations[binaryCollation] {
|
|
||||||
return "MEDIUMTEXT"
|
|
||||||
}
|
|
||||||
return "MEDIUMBLOB"
|
|
||||||
case fieldTypeNewDate:
|
|
||||||
return "DATE"
|
|
||||||
case fieldTypeNewDecimal:
|
|
||||||
return "DECIMAL"
|
|
||||||
case fieldTypeNULL:
|
|
||||||
return "NULL"
|
|
||||||
case fieldTypeSet:
|
|
||||||
return "SET"
|
|
||||||
case fieldTypeShort:
|
|
||||||
if mf.flags&flagUnsigned != 0 {
|
|
||||||
return "UNSIGNED SMALLINT"
|
|
||||||
}
|
|
||||||
return "SMALLINT"
|
|
||||||
case fieldTypeString:
|
|
||||||
if mf.charSet == collations[binaryCollation] {
|
|
||||||
return "BINARY"
|
|
||||||
}
|
|
||||||
return "CHAR"
|
|
||||||
case fieldTypeTime:
|
|
||||||
return "TIME"
|
|
||||||
case fieldTypeTimestamp:
|
|
||||||
return "TIMESTAMP"
|
|
||||||
case fieldTypeTiny:
|
|
||||||
if mf.flags&flagUnsigned != 0 {
|
|
||||||
return "UNSIGNED TINYINT"
|
|
||||||
}
|
|
||||||
return "TINYINT"
|
|
||||||
case fieldTypeTinyBLOB:
|
|
||||||
if mf.charSet != collations[binaryCollation] {
|
|
||||||
return "TINYTEXT"
|
|
||||||
}
|
|
||||||
return "TINYBLOB"
|
|
||||||
case fieldTypeVarChar:
|
|
||||||
if mf.charSet == collations[binaryCollation] {
|
|
||||||
return "VARBINARY"
|
|
||||||
}
|
|
||||||
return "VARCHAR"
|
|
||||||
case fieldTypeVarString:
|
|
||||||
if mf.charSet == collations[binaryCollation] {
|
|
||||||
return "VARBINARY"
|
|
||||||
}
|
|
||||||
return "VARCHAR"
|
|
||||||
case fieldTypeYear:
|
|
||||||
return "YEAR"
|
|
||||||
default:
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
scanTypeFloat32 = reflect.TypeOf(float32(0))
|
|
||||||
scanTypeFloat64 = reflect.TypeOf(float64(0))
|
|
||||||
scanTypeInt8 = reflect.TypeOf(int8(0))
|
|
||||||
scanTypeInt16 = reflect.TypeOf(int16(0))
|
|
||||||
scanTypeInt32 = reflect.TypeOf(int32(0))
|
|
||||||
scanTypeInt64 = reflect.TypeOf(int64(0))
|
|
||||||
scanTypeNullFloat = reflect.TypeOf(sql.NullFloat64{})
|
|
||||||
scanTypeNullInt = reflect.TypeOf(sql.NullInt64{})
|
|
||||||
scanTypeNullTime = reflect.TypeOf(sql.NullTime{})
|
|
||||||
scanTypeUint8 = reflect.TypeOf(uint8(0))
|
|
||||||
scanTypeUint16 = reflect.TypeOf(uint16(0))
|
|
||||||
scanTypeUint32 = reflect.TypeOf(uint32(0))
|
|
||||||
scanTypeUint64 = reflect.TypeOf(uint64(0))
|
|
||||||
scanTypeRawBytes = reflect.TypeOf(sql.RawBytes{})
|
|
||||||
scanTypeUnknown = reflect.TypeOf(new(interface{}))
|
|
||||||
)
|
|
||||||
|
|
||||||
type mysqlField struct {
|
|
||||||
tableName string
|
|
||||||
name string
|
|
||||||
length uint32
|
|
||||||
flags fieldFlag
|
|
||||||
fieldType fieldType
|
|
||||||
decimals byte
|
|
||||||
charSet uint8
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mf *mysqlField) scanType() reflect.Type {
|
|
||||||
switch mf.fieldType {
|
|
||||||
case fieldTypeTiny:
|
|
||||||
if mf.flags&flagNotNULL != 0 {
|
|
||||||
if mf.flags&flagUnsigned != 0 {
|
|
||||||
return scanTypeUint8
|
|
||||||
}
|
|
||||||
return scanTypeInt8
|
|
||||||
}
|
|
||||||
return scanTypeNullInt
|
|
||||||
|
|
||||||
case fieldTypeShort, fieldTypeYear:
|
|
||||||
if mf.flags&flagNotNULL != 0 {
|
|
||||||
if mf.flags&flagUnsigned != 0 {
|
|
||||||
return scanTypeUint16
|
|
||||||
}
|
|
||||||
return scanTypeInt16
|
|
||||||
}
|
|
||||||
return scanTypeNullInt
|
|
||||||
|
|
||||||
case fieldTypeInt24, fieldTypeLong:
|
|
||||||
if mf.flags&flagNotNULL != 0 {
|
|
||||||
if mf.flags&flagUnsigned != 0 {
|
|
||||||
return scanTypeUint32
|
|
||||||
}
|
|
||||||
return scanTypeInt32
|
|
||||||
}
|
|
||||||
return scanTypeNullInt
|
|
||||||
|
|
||||||
case fieldTypeLongLong:
|
|
||||||
if mf.flags&flagNotNULL != 0 {
|
|
||||||
if mf.flags&flagUnsigned != 0 {
|
|
||||||
return scanTypeUint64
|
|
||||||
}
|
|
||||||
return scanTypeInt64
|
|
||||||
}
|
|
||||||
return scanTypeNullInt
|
|
||||||
|
|
||||||
case fieldTypeFloat:
|
|
||||||
if mf.flags&flagNotNULL != 0 {
|
|
||||||
return scanTypeFloat32
|
|
||||||
}
|
|
||||||
return scanTypeNullFloat
|
|
||||||
|
|
||||||
case fieldTypeDouble:
|
|
||||||
if mf.flags&flagNotNULL != 0 {
|
|
||||||
return scanTypeFloat64
|
|
||||||
}
|
|
||||||
return scanTypeNullFloat
|
|
||||||
|
|
||||||
case fieldTypeDecimal, fieldTypeNewDecimal, fieldTypeVarChar,
|
|
||||||
fieldTypeBit, fieldTypeEnum, fieldTypeSet, fieldTypeTinyBLOB,
|
|
||||||
fieldTypeMediumBLOB, fieldTypeLongBLOB, fieldTypeBLOB,
|
|
||||||
fieldTypeVarString, fieldTypeString, fieldTypeGeometry, fieldTypeJSON,
|
|
||||||
fieldTypeTime:
|
|
||||||
return scanTypeRawBytes
|
|
||||||
|
|
||||||
case fieldTypeDate, fieldTypeNewDate,
|
|
||||||
fieldTypeTimestamp, fieldTypeDateTime:
|
|
||||||
// NullTime is always returned for more consistent behavior as it can
|
|
||||||
// handle both cases of parseTime regardless if the field is nullable.
|
|
||||||
return scanTypeNullTime
|
|
||||||
|
|
||||||
default:
|
|
||||||
return scanTypeUnknown
|
|
||||||
}
|
|
||||||
}
|
|
||||||
25
vendor/github.com/go-sql-driver/mysql/fuzz.go
generated
vendored
25
vendor/github.com/go-sql-driver/mysql/fuzz.go
generated
vendored
@@ -1,25 +0,0 @@
|
|||||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package.
|
|
||||||
//
|
|
||||||
// Copyright 2020 The Go-MySQL-Driver Authors. All rights reserved.
|
|
||||||
//
|
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
||||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
|
|
||||||
//go:build gofuzz
|
|
||||||
// +build gofuzz
|
|
||||||
|
|
||||||
package mysql
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Fuzz(data []byte) int {
|
|
||||||
db, err := sql.Open("mysql", string(data))
|
|
||||||
if err != nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
db.Close()
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
182
vendor/github.com/go-sql-driver/mysql/infile.go
generated
vendored
182
vendor/github.com/go-sql-driver/mysql/infile.go
generated
vendored
@@ -1,182 +0,0 @@
|
|||||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
|
||||||
//
|
|
||||||
// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved.
|
|
||||||
//
|
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
||||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
|
|
||||||
package mysql
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
fileRegister map[string]bool
|
|
||||||
fileRegisterLock sync.RWMutex
|
|
||||||
readerRegister map[string]func() io.Reader
|
|
||||||
readerRegisterLock sync.RWMutex
|
|
||||||
)
|
|
||||||
|
|
||||||
// RegisterLocalFile adds the given file to the file allowlist,
|
|
||||||
// so that it can be used by "LOAD DATA LOCAL INFILE <filepath>".
|
|
||||||
// Alternatively you can allow the use of all local files with
|
|
||||||
// the DSN parameter 'allowAllFiles=true'
|
|
||||||
//
|
|
||||||
// filePath := "/home/gopher/data.csv"
|
|
||||||
// mysql.RegisterLocalFile(filePath)
|
|
||||||
// err := db.Exec("LOAD DATA LOCAL INFILE '" + filePath + "' INTO TABLE foo")
|
|
||||||
// if err != nil {
|
|
||||||
// ...
|
|
||||||
func RegisterLocalFile(filePath string) {
|
|
||||||
fileRegisterLock.Lock()
|
|
||||||
// lazy map init
|
|
||||||
if fileRegister == nil {
|
|
||||||
fileRegister = make(map[string]bool)
|
|
||||||
}
|
|
||||||
|
|
||||||
fileRegister[strings.Trim(filePath, `"`)] = true
|
|
||||||
fileRegisterLock.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeregisterLocalFile removes the given filepath from the allowlist.
|
|
||||||
func DeregisterLocalFile(filePath string) {
|
|
||||||
fileRegisterLock.Lock()
|
|
||||||
delete(fileRegister, strings.Trim(filePath, `"`))
|
|
||||||
fileRegisterLock.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterReaderHandler registers a handler function which is used
|
|
||||||
// to receive a io.Reader.
|
|
||||||
// The Reader can be used by "LOAD DATA LOCAL INFILE Reader::<name>".
|
|
||||||
// If the handler returns a io.ReadCloser Close() is called when the
|
|
||||||
// request is finished.
|
|
||||||
//
|
|
||||||
// mysql.RegisterReaderHandler("data", func() io.Reader {
|
|
||||||
// var csvReader io.Reader // Some Reader that returns CSV data
|
|
||||||
// ... // Open Reader here
|
|
||||||
// return csvReader
|
|
||||||
// })
|
|
||||||
// err := db.Exec("LOAD DATA LOCAL INFILE 'Reader::data' INTO TABLE foo")
|
|
||||||
// if err != nil {
|
|
||||||
// ...
|
|
||||||
func RegisterReaderHandler(name string, handler func() io.Reader) {
|
|
||||||
readerRegisterLock.Lock()
|
|
||||||
// lazy map init
|
|
||||||
if readerRegister == nil {
|
|
||||||
readerRegister = make(map[string]func() io.Reader)
|
|
||||||
}
|
|
||||||
|
|
||||||
readerRegister[name] = handler
|
|
||||||
readerRegisterLock.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeregisterReaderHandler removes the ReaderHandler function with
|
|
||||||
// the given name from the registry.
|
|
||||||
func DeregisterReaderHandler(name string) {
|
|
||||||
readerRegisterLock.Lock()
|
|
||||||
delete(readerRegister, name)
|
|
||||||
readerRegisterLock.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func deferredClose(err *error, closer io.Closer) {
|
|
||||||
closeErr := closer.Close()
|
|
||||||
if *err == nil {
|
|
||||||
*err = closeErr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const defaultPacketSize = 16 * 1024 // 16KB is small enough for disk readahead and large enough for TCP
|
|
||||||
|
|
||||||
func (mc *mysqlConn) handleInFileRequest(name string) (err error) {
|
|
||||||
var rdr io.Reader
|
|
||||||
var data []byte
|
|
||||||
packetSize := defaultPacketSize
|
|
||||||
if mc.maxWriteSize < packetSize {
|
|
||||||
packetSize = mc.maxWriteSize
|
|
||||||
}
|
|
||||||
|
|
||||||
if idx := strings.Index(name, "Reader::"); idx == 0 || (idx > 0 && name[idx-1] == '/') { // io.Reader
|
|
||||||
// The server might return an an absolute path. See issue #355.
|
|
||||||
name = name[idx+8:]
|
|
||||||
|
|
||||||
readerRegisterLock.RLock()
|
|
||||||
handler, inMap := readerRegister[name]
|
|
||||||
readerRegisterLock.RUnlock()
|
|
||||||
|
|
||||||
if inMap {
|
|
||||||
rdr = handler()
|
|
||||||
if rdr != nil {
|
|
||||||
if cl, ok := rdr.(io.Closer); ok {
|
|
||||||
defer deferredClose(&err, cl)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = fmt.Errorf("Reader '%s' is <nil>", name)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = fmt.Errorf("Reader '%s' is not registered", name)
|
|
||||||
}
|
|
||||||
} else { // File
|
|
||||||
name = strings.Trim(name, `"`)
|
|
||||||
fileRegisterLock.RLock()
|
|
||||||
fr := fileRegister[name]
|
|
||||||
fileRegisterLock.RUnlock()
|
|
||||||
if mc.cfg.AllowAllFiles || fr {
|
|
||||||
var file *os.File
|
|
||||||
var fi os.FileInfo
|
|
||||||
|
|
||||||
if file, err = os.Open(name); err == nil {
|
|
||||||
defer deferredClose(&err, file)
|
|
||||||
|
|
||||||
// get file size
|
|
||||||
if fi, err = file.Stat(); err == nil {
|
|
||||||
rdr = file
|
|
||||||
if fileSize := int(fi.Size()); fileSize < packetSize {
|
|
||||||
packetSize = fileSize
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
err = fmt.Errorf("local file '%s' is not registered", name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// send content packets
|
|
||||||
// if packetSize == 0, the Reader contains no data
|
|
||||||
if err == nil && packetSize > 0 {
|
|
||||||
data := make([]byte, 4+packetSize)
|
|
||||||
var n int
|
|
||||||
for err == nil {
|
|
||||||
n, err = rdr.Read(data[4:])
|
|
||||||
if n > 0 {
|
|
||||||
if ioErr := mc.writePacket(data[:4+n]); ioErr != nil {
|
|
||||||
return ioErr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err == io.EOF {
|
|
||||||
err = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// send empty packet (termination)
|
|
||||||
if data == nil {
|
|
||||||
data = make([]byte, 4)
|
|
||||||
}
|
|
||||||
if ioErr := mc.writePacket(data[:4]); ioErr != nil {
|
|
||||||
return ioErr
|
|
||||||
}
|
|
||||||
|
|
||||||
// read OK packet
|
|
||||||
if err == nil {
|
|
||||||
return mc.readResultOK()
|
|
||||||
}
|
|
||||||
|
|
||||||
mc.readPacket()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
71
vendor/github.com/go-sql-driver/mysql/nulltime.go
generated
vendored
71
vendor/github.com/go-sql-driver/mysql/nulltime.go
generated
vendored
@@ -1,71 +0,0 @@
|
|||||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
|
||||||
//
|
|
||||||
// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved.
|
|
||||||
//
|
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
||||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
|
|
||||||
package mysql
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql"
|
|
||||||
"database/sql/driver"
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NullTime represents a time.Time that may be NULL.
|
|
||||||
// NullTime implements the Scanner interface so
|
|
||||||
// it can be used as a scan destination:
|
|
||||||
//
|
|
||||||
// var nt NullTime
|
|
||||||
// err := db.QueryRow("SELECT time FROM foo WHERE id=?", id).Scan(&nt)
|
|
||||||
// ...
|
|
||||||
// if nt.Valid {
|
|
||||||
// // use nt.Time
|
|
||||||
// } else {
|
|
||||||
// // NULL value
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// # This NullTime implementation is not driver-specific
|
|
||||||
//
|
|
||||||
// Deprecated: NullTime doesn't honor the loc DSN parameter.
|
|
||||||
// NullTime.Scan interprets a time as UTC, not the loc DSN parameter.
|
|
||||||
// Use sql.NullTime instead.
|
|
||||||
type NullTime sql.NullTime
|
|
||||||
|
|
||||||
// Scan implements the Scanner interface.
|
|
||||||
// The value type must be time.Time or string / []byte (formatted time-string),
|
|
||||||
// otherwise Scan fails.
|
|
||||||
func (nt *NullTime) Scan(value interface{}) (err error) {
|
|
||||||
if value == nil {
|
|
||||||
nt.Time, nt.Valid = time.Time{}, false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
switch v := value.(type) {
|
|
||||||
case time.Time:
|
|
||||||
nt.Time, nt.Valid = v, true
|
|
||||||
return
|
|
||||||
case []byte:
|
|
||||||
nt.Time, err = parseDateTime(v, time.UTC)
|
|
||||||
nt.Valid = (err == nil)
|
|
||||||
return
|
|
||||||
case string:
|
|
||||||
nt.Time, err = parseDateTime([]byte(v), time.UTC)
|
|
||||||
nt.Valid = (err == nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
nt.Valid = false
|
|
||||||
return fmt.Errorf("Can't convert %T to time.Time", value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Value implements the driver Valuer interface.
|
|
||||||
func (nt NullTime) Value() (driver.Value, error) {
|
|
||||||
if !nt.Valid {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return nt.Time, nil
|
|
||||||
}
|
|
||||||
1349
vendor/github.com/go-sql-driver/mysql/packets.go
generated
vendored
1349
vendor/github.com/go-sql-driver/mysql/packets.go
generated
vendored
File diff suppressed because it is too large
Load Diff
22
vendor/github.com/go-sql-driver/mysql/result.go
generated
vendored
22
vendor/github.com/go-sql-driver/mysql/result.go
generated
vendored
@@ -1,22 +0,0 @@
|
|||||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
|
||||||
//
|
|
||||||
// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
|
|
||||||
//
|
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
||||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
|
|
||||||
package mysql
|
|
||||||
|
|
||||||
type mysqlResult struct {
|
|
||||||
affectedRows int64
|
|
||||||
insertId int64
|
|
||||||
}
|
|
||||||
|
|
||||||
func (res *mysqlResult) LastInsertId() (int64, error) {
|
|
||||||
return res.insertId, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (res *mysqlResult) RowsAffected() (int64, error) {
|
|
||||||
return res.affectedRows, nil
|
|
||||||
}
|
|
||||||
223
vendor/github.com/go-sql-driver/mysql/rows.go
generated
vendored
223
vendor/github.com/go-sql-driver/mysql/rows.go
generated
vendored
@@ -1,223 +0,0 @@
|
|||||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
|
||||||
//
|
|
||||||
// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
|
|
||||||
//
|
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
||||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
|
|
||||||
package mysql
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql/driver"
|
|
||||||
"io"
|
|
||||||
"math"
|
|
||||||
"reflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
type resultSet struct {
|
|
||||||
columns []mysqlField
|
|
||||||
columnNames []string
|
|
||||||
done bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type mysqlRows struct {
|
|
||||||
mc *mysqlConn
|
|
||||||
rs resultSet
|
|
||||||
finish func()
|
|
||||||
}
|
|
||||||
|
|
||||||
type binaryRows struct {
|
|
||||||
mysqlRows
|
|
||||||
}
|
|
||||||
|
|
||||||
type textRows struct {
|
|
||||||
mysqlRows
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rows *mysqlRows) Columns() []string {
|
|
||||||
if rows.rs.columnNames != nil {
|
|
||||||
return rows.rs.columnNames
|
|
||||||
}
|
|
||||||
|
|
||||||
columns := make([]string, len(rows.rs.columns))
|
|
||||||
if rows.mc != nil && rows.mc.cfg.ColumnsWithAlias {
|
|
||||||
for i := range columns {
|
|
||||||
if tableName := rows.rs.columns[i].tableName; len(tableName) > 0 {
|
|
||||||
columns[i] = tableName + "." + rows.rs.columns[i].name
|
|
||||||
} else {
|
|
||||||
columns[i] = rows.rs.columns[i].name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for i := range columns {
|
|
||||||
columns[i] = rows.rs.columns[i].name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rows.rs.columnNames = columns
|
|
||||||
return columns
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rows *mysqlRows) ColumnTypeDatabaseTypeName(i int) string {
|
|
||||||
return rows.rs.columns[i].typeDatabaseName()
|
|
||||||
}
|
|
||||||
|
|
||||||
// func (rows *mysqlRows) ColumnTypeLength(i int) (length int64, ok bool) {
|
|
||||||
// return int64(rows.rs.columns[i].length), true
|
|
||||||
// }
|
|
||||||
|
|
||||||
func (rows *mysqlRows) ColumnTypeNullable(i int) (nullable, ok bool) {
|
|
||||||
return rows.rs.columns[i].flags&flagNotNULL == 0, true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rows *mysqlRows) ColumnTypePrecisionScale(i int) (int64, int64, bool) {
|
|
||||||
column := rows.rs.columns[i]
|
|
||||||
decimals := int64(column.decimals)
|
|
||||||
|
|
||||||
switch column.fieldType {
|
|
||||||
case fieldTypeDecimal, fieldTypeNewDecimal:
|
|
||||||
if decimals > 0 {
|
|
||||||
return int64(column.length) - 2, decimals, true
|
|
||||||
}
|
|
||||||
return int64(column.length) - 1, decimals, true
|
|
||||||
case fieldTypeTimestamp, fieldTypeDateTime, fieldTypeTime:
|
|
||||||
return decimals, decimals, true
|
|
||||||
case fieldTypeFloat, fieldTypeDouble:
|
|
||||||
if decimals == 0x1f {
|
|
||||||
return math.MaxInt64, math.MaxInt64, true
|
|
||||||
}
|
|
||||||
return math.MaxInt64, decimals, true
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0, 0, false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rows *mysqlRows) ColumnTypeScanType(i int) reflect.Type {
|
|
||||||
return rows.rs.columns[i].scanType()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rows *mysqlRows) Close() (err error) {
|
|
||||||
if f := rows.finish; f != nil {
|
|
||||||
f()
|
|
||||||
rows.finish = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
mc := rows.mc
|
|
||||||
if mc == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if err := mc.error(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// flip the buffer for this connection if we need to drain it.
|
|
||||||
// note that for a successful query (i.e. one where rows.next()
|
|
||||||
// has been called until it returns false), `rows.mc` will be nil
|
|
||||||
// by the time the user calls `(*Rows).Close`, so we won't reach this
|
|
||||||
// see: https://github.com/golang/go/commit/651ddbdb5056ded455f47f9c494c67b389622a47
|
|
||||||
mc.buf.flip()
|
|
||||||
|
|
||||||
// Remove unread packets from stream
|
|
||||||
if !rows.rs.done {
|
|
||||||
err = mc.readUntilEOF()
|
|
||||||
}
|
|
||||||
if err == nil {
|
|
||||||
if err = mc.discardResults(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rows.mc = nil
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rows *mysqlRows) HasNextResultSet() (b bool) {
|
|
||||||
if rows.mc == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return rows.mc.status&statusMoreResultsExists != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rows *mysqlRows) nextResultSet() (int, error) {
|
|
||||||
if rows.mc == nil {
|
|
||||||
return 0, io.EOF
|
|
||||||
}
|
|
||||||
if err := rows.mc.error(); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove unread packets from stream
|
|
||||||
if !rows.rs.done {
|
|
||||||
if err := rows.mc.readUntilEOF(); err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
rows.rs.done = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if !rows.HasNextResultSet() {
|
|
||||||
rows.mc = nil
|
|
||||||
return 0, io.EOF
|
|
||||||
}
|
|
||||||
rows.rs = resultSet{}
|
|
||||||
return rows.mc.readResultSetHeaderPacket()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rows *mysqlRows) nextNotEmptyResultSet() (int, error) {
|
|
||||||
for {
|
|
||||||
resLen, err := rows.nextResultSet()
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if resLen > 0 {
|
|
||||||
return resLen, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
rows.rs.done = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rows *binaryRows) NextResultSet() error {
|
|
||||||
resLen, err := rows.nextNotEmptyResultSet()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
rows.rs.columns, err = rows.mc.readColumns(resLen)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rows *binaryRows) Next(dest []driver.Value) error {
|
|
||||||
if mc := rows.mc; mc != nil {
|
|
||||||
if err := mc.error(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch next row from stream
|
|
||||||
return rows.readRow(dest)
|
|
||||||
}
|
|
||||||
return io.EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rows *textRows) NextResultSet() (err error) {
|
|
||||||
resLen, err := rows.nextNotEmptyResultSet()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
rows.rs.columns, err = rows.mc.readColumns(resLen)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rows *textRows) Next(dest []driver.Value) error {
|
|
||||||
if mc := rows.mc; mc != nil {
|
|
||||||
if err := mc.error(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch next row from stream
|
|
||||||
return rows.readRow(dest)
|
|
||||||
}
|
|
||||||
return io.EOF
|
|
||||||
}
|
|
||||||
220
vendor/github.com/go-sql-driver/mysql/statement.go
generated
vendored
220
vendor/github.com/go-sql-driver/mysql/statement.go
generated
vendored
@@ -1,220 +0,0 @@
|
|||||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
|
||||||
//
|
|
||||||
// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
|
|
||||||
//
|
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
||||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
|
|
||||||
package mysql
|
|
||||||
|
|
||||||
import (
|
|
||||||
"database/sql/driver"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"reflect"
|
|
||||||
)
|
|
||||||
|
|
||||||
type mysqlStmt struct {
|
|
||||||
mc *mysqlConn
|
|
||||||
id uint32
|
|
||||||
paramCount int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (stmt *mysqlStmt) Close() error {
|
|
||||||
if stmt.mc == nil || stmt.mc.closed.Load() {
|
|
||||||
// driver.Stmt.Close can be called more than once, thus this function
|
|
||||||
// has to be idempotent.
|
|
||||||
// See also Issue #450 and golang/go#16019.
|
|
||||||
//errLog.Print(ErrInvalidConn)
|
|
||||||
return driver.ErrBadConn
|
|
||||||
}
|
|
||||||
|
|
||||||
err := stmt.mc.writeCommandPacketUint32(comStmtClose, stmt.id)
|
|
||||||
stmt.mc = nil
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (stmt *mysqlStmt) NumInput() int {
|
|
||||||
return stmt.paramCount
|
|
||||||
}
|
|
||||||
|
|
||||||
func (stmt *mysqlStmt) ColumnConverter(idx int) driver.ValueConverter {
|
|
||||||
return converter{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (stmt *mysqlStmt) CheckNamedValue(nv *driver.NamedValue) (err error) {
|
|
||||||
nv.Value, err = converter{}.ConvertValue(nv.Value)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (stmt *mysqlStmt) Exec(args []driver.Value) (driver.Result, error) {
|
|
||||||
if stmt.mc.closed.Load() {
|
|
||||||
errLog.Print(ErrInvalidConn)
|
|
||||||
return nil, driver.ErrBadConn
|
|
||||||
}
|
|
||||||
// Send command
|
|
||||||
err := stmt.writeExecutePacket(args)
|
|
||||||
if err != nil {
|
|
||||||
return nil, stmt.mc.markBadConn(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
mc := stmt.mc
|
|
||||||
|
|
||||||
mc.affectedRows = 0
|
|
||||||
mc.insertId = 0
|
|
||||||
|
|
||||||
// Read Result
|
|
||||||
resLen, err := mc.readResultSetHeaderPacket()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if resLen > 0 {
|
|
||||||
// Columns
|
|
||||||
if err = mc.readUntilEOF(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Rows
|
|
||||||
if err := mc.readUntilEOF(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := mc.discardResults(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &mysqlResult{
|
|
||||||
affectedRows: int64(mc.affectedRows),
|
|
||||||
insertId: int64(mc.insertId),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (stmt *mysqlStmt) Query(args []driver.Value) (driver.Rows, error) {
|
|
||||||
return stmt.query(args)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (stmt *mysqlStmt) query(args []driver.Value) (*binaryRows, error) {
|
|
||||||
if stmt.mc.closed.Load() {
|
|
||||||
errLog.Print(ErrInvalidConn)
|
|
||||||
return nil, driver.ErrBadConn
|
|
||||||
}
|
|
||||||
// Send command
|
|
||||||
err := stmt.writeExecutePacket(args)
|
|
||||||
if err != nil {
|
|
||||||
return nil, stmt.mc.markBadConn(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
mc := stmt.mc
|
|
||||||
|
|
||||||
// Read Result
|
|
||||||
resLen, err := mc.readResultSetHeaderPacket()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
rows := new(binaryRows)
|
|
||||||
|
|
||||||
if resLen > 0 {
|
|
||||||
rows.mc = mc
|
|
||||||
rows.rs.columns, err = mc.readColumns(resLen)
|
|
||||||
} else {
|
|
||||||
rows.rs.done = true
|
|
||||||
|
|
||||||
switch err := rows.NextResultSet(); err {
|
|
||||||
case nil, io.EOF:
|
|
||||||
return rows, nil
|
|
||||||
default:
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return rows, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var jsonType = reflect.TypeOf(json.RawMessage{})
|
|
||||||
|
|
||||||
type converter struct{}
|
|
||||||
|
|
||||||
// ConvertValue mirrors the reference/default converter in database/sql/driver
|
|
||||||
// with _one_ exception. We support uint64 with their high bit and the default
|
|
||||||
// implementation does not. This function should be kept in sync with
|
|
||||||
// database/sql/driver defaultConverter.ConvertValue() except for that
|
|
||||||
// deliberate difference.
|
|
||||||
func (c converter) ConvertValue(v interface{}) (driver.Value, error) {
|
|
||||||
if driver.IsValue(v) {
|
|
||||||
return v, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if vr, ok := v.(driver.Valuer); ok {
|
|
||||||
sv, err := callValuerValue(vr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if driver.IsValue(sv) {
|
|
||||||
return sv, nil
|
|
||||||
}
|
|
||||||
// A value returned from the Valuer interface can be "a type handled by
|
|
||||||
// a database driver's NamedValueChecker interface" so we should accept
|
|
||||||
// uint64 here as well.
|
|
||||||
if u, ok := sv.(uint64); ok {
|
|
||||||
return u, nil
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("non-Value type %T returned from Value", sv)
|
|
||||||
}
|
|
||||||
rv := reflect.ValueOf(v)
|
|
||||||
switch rv.Kind() {
|
|
||||||
case reflect.Ptr:
|
|
||||||
// indirect pointers
|
|
||||||
if rv.IsNil() {
|
|
||||||
return nil, nil
|
|
||||||
} else {
|
|
||||||
return c.ConvertValue(rv.Elem().Interface())
|
|
||||||
}
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
||||||
return rv.Int(), nil
|
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
||||||
return rv.Uint(), nil
|
|
||||||
case reflect.Float32, reflect.Float64:
|
|
||||||
return rv.Float(), nil
|
|
||||||
case reflect.Bool:
|
|
||||||
return rv.Bool(), nil
|
|
||||||
case reflect.Slice:
|
|
||||||
switch t := rv.Type(); {
|
|
||||||
case t == jsonType:
|
|
||||||
return v, nil
|
|
||||||
case t.Elem().Kind() == reflect.Uint8:
|
|
||||||
return rv.Bytes(), nil
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("unsupported type %T, a slice of %s", v, t.Elem().Kind())
|
|
||||||
}
|
|
||||||
case reflect.String:
|
|
||||||
return rv.String(), nil
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("unsupported type %T, a %s", v, rv.Kind())
|
|
||||||
}
|
|
||||||
|
|
||||||
var valuerReflectType = reflect.TypeOf((*driver.Valuer)(nil)).Elem()
|
|
||||||
|
|
||||||
// callValuerValue returns vr.Value(), with one exception:
|
|
||||||
// If vr.Value is an auto-generated method on a pointer type and the
|
|
||||||
// pointer is nil, it would panic at runtime in the panicwrap
|
|
||||||
// method. Treat it like nil instead.
|
|
||||||
//
|
|
||||||
// This is so people can implement driver.Value on value types and
|
|
||||||
// still use nil pointers to those types to mean nil/NULL, just like
|
|
||||||
// string/*string.
|
|
||||||
//
|
|
||||||
// This is an exact copy of the same-named unexported function from the
|
|
||||||
// database/sql package.
|
|
||||||
func callValuerValue(vr driver.Valuer) (v driver.Value, err error) {
|
|
||||||
if rv := reflect.ValueOf(vr); rv.Kind() == reflect.Ptr &&
|
|
||||||
rv.IsNil() &&
|
|
||||||
rv.Type().Elem().Implements(valuerReflectType) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return vr.Value()
|
|
||||||
}
|
|
||||||
31
vendor/github.com/go-sql-driver/mysql/transaction.go
generated
vendored
31
vendor/github.com/go-sql-driver/mysql/transaction.go
generated
vendored
@@ -1,31 +0,0 @@
|
|||||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
|
||||||
//
|
|
||||||
// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
|
|
||||||
//
|
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
||||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
|
|
||||||
package mysql
|
|
||||||
|
|
||||||
type mysqlTx struct {
|
|
||||||
mc *mysqlConn
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tx *mysqlTx) Commit() (err error) {
|
|
||||||
if tx.mc == nil || tx.mc.closed.Load() {
|
|
||||||
return ErrInvalidConn
|
|
||||||
}
|
|
||||||
err = tx.mc.exec("COMMIT")
|
|
||||||
tx.mc = nil
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tx *mysqlTx) Rollback() (err error) {
|
|
||||||
if tx.mc == nil || tx.mc.closed.Load() {
|
|
||||||
return ErrInvalidConn
|
|
||||||
}
|
|
||||||
err = tx.mc.exec("ROLLBACK")
|
|
||||||
tx.mc = nil
|
|
||||||
return
|
|
||||||
}
|
|
||||||
834
vendor/github.com/go-sql-driver/mysql/utils.go
generated
vendored
834
vendor/github.com/go-sql-driver/mysql/utils.go
generated
vendored
@@ -1,834 +0,0 @@
|
|||||||
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
|
|
||||||
//
|
|
||||||
// Copyright 2012 The Go-MySQL-Driver Authors. All rights reserved.
|
|
||||||
//
|
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
||||||
// You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
|
|
||||||
package mysql
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/tls"
|
|
||||||
"database/sql"
|
|
||||||
"database/sql/driver"
|
|
||||||
"encoding/binary"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Registry for custom tls.Configs
|
|
||||||
var (
|
|
||||||
tlsConfigLock sync.RWMutex
|
|
||||||
tlsConfigRegistry map[string]*tls.Config
|
|
||||||
)
|
|
||||||
|
|
||||||
// RegisterTLSConfig registers a custom tls.Config to be used with sql.Open.
|
|
||||||
// Use the key as a value in the DSN where tls=value.
|
|
||||||
//
|
|
||||||
// Note: The provided tls.Config is exclusively owned by the driver after
|
|
||||||
// registering it.
|
|
||||||
//
|
|
||||||
// rootCertPool := x509.NewCertPool()
|
|
||||||
// pem, err := ioutil.ReadFile("/path/ca-cert.pem")
|
|
||||||
// if err != nil {
|
|
||||||
// log.Fatal(err)
|
|
||||||
// }
|
|
||||||
// if ok := rootCertPool.AppendCertsFromPEM(pem); !ok {
|
|
||||||
// log.Fatal("Failed to append PEM.")
|
|
||||||
// }
|
|
||||||
// clientCert := make([]tls.Certificate, 0, 1)
|
|
||||||
// certs, err := tls.LoadX509KeyPair("/path/client-cert.pem", "/path/client-key.pem")
|
|
||||||
// if err != nil {
|
|
||||||
// log.Fatal(err)
|
|
||||||
// }
|
|
||||||
// clientCert = append(clientCert, certs)
|
|
||||||
// mysql.RegisterTLSConfig("custom", &tls.Config{
|
|
||||||
// RootCAs: rootCertPool,
|
|
||||||
// Certificates: clientCert,
|
|
||||||
// })
|
|
||||||
// db, err := sql.Open("mysql", "user@tcp(localhost:3306)/test?tls=custom")
|
|
||||||
func RegisterTLSConfig(key string, config *tls.Config) error {
|
|
||||||
if _, isBool := readBool(key); isBool || strings.ToLower(key) == "skip-verify" || strings.ToLower(key) == "preferred" {
|
|
||||||
return fmt.Errorf("key '%s' is reserved", key)
|
|
||||||
}
|
|
||||||
|
|
||||||
tlsConfigLock.Lock()
|
|
||||||
if tlsConfigRegistry == nil {
|
|
||||||
tlsConfigRegistry = make(map[string]*tls.Config)
|
|
||||||
}
|
|
||||||
|
|
||||||
tlsConfigRegistry[key] = config
|
|
||||||
tlsConfigLock.Unlock()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeregisterTLSConfig removes the tls.Config associated with key.
|
|
||||||
func DeregisterTLSConfig(key string) {
|
|
||||||
tlsConfigLock.Lock()
|
|
||||||
if tlsConfigRegistry != nil {
|
|
||||||
delete(tlsConfigRegistry, key)
|
|
||||||
}
|
|
||||||
tlsConfigLock.Unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
func getTLSConfigClone(key string) (config *tls.Config) {
|
|
||||||
tlsConfigLock.RLock()
|
|
||||||
if v, ok := tlsConfigRegistry[key]; ok {
|
|
||||||
config = v.Clone()
|
|
||||||
}
|
|
||||||
tlsConfigLock.RUnlock()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the bool value of the input.
|
|
||||||
// The 2nd return value indicates if the input was a valid bool value
|
|
||||||
func readBool(input string) (value bool, valid bool) {
|
|
||||||
switch input {
|
|
||||||
case "1", "true", "TRUE", "True":
|
|
||||||
return true, true
|
|
||||||
case "0", "false", "FALSE", "False":
|
|
||||||
return false, true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Not a valid bool value
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************************************************************
|
|
||||||
* Time related utils *
|
|
||||||
******************************************************************************/
|
|
||||||
|
|
||||||
func parseDateTime(b []byte, loc *time.Location) (time.Time, error) {
|
|
||||||
const base = "0000-00-00 00:00:00.000000"
|
|
||||||
switch len(b) {
|
|
||||||
case 10, 19, 21, 22, 23, 24, 25, 26: // up to "YYYY-MM-DD HH:MM:SS.MMMMMM"
|
|
||||||
if string(b) == base[:len(b)] {
|
|
||||||
return time.Time{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
year, err := parseByteYear(b)
|
|
||||||
if err != nil {
|
|
||||||
return time.Time{}, err
|
|
||||||
}
|
|
||||||
if b[4] != '-' {
|
|
||||||
return time.Time{}, fmt.Errorf("bad value for field: `%c`", b[4])
|
|
||||||
}
|
|
||||||
|
|
||||||
m, err := parseByte2Digits(b[5], b[6])
|
|
||||||
if err != nil {
|
|
||||||
return time.Time{}, err
|
|
||||||
}
|
|
||||||
month := time.Month(m)
|
|
||||||
|
|
||||||
if b[7] != '-' {
|
|
||||||
return time.Time{}, fmt.Errorf("bad value for field: `%c`", b[7])
|
|
||||||
}
|
|
||||||
|
|
||||||
day, err := parseByte2Digits(b[8], b[9])
|
|
||||||
if err != nil {
|
|
||||||
return time.Time{}, err
|
|
||||||
}
|
|
||||||
if len(b) == 10 {
|
|
||||||
return time.Date(year, month, day, 0, 0, 0, 0, loc), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if b[10] != ' ' {
|
|
||||||
return time.Time{}, fmt.Errorf("bad value for field: `%c`", b[10])
|
|
||||||
}
|
|
||||||
|
|
||||||
hour, err := parseByte2Digits(b[11], b[12])
|
|
||||||
if err != nil {
|
|
||||||
return time.Time{}, err
|
|
||||||
}
|
|
||||||
if b[13] != ':' {
|
|
||||||
return time.Time{}, fmt.Errorf("bad value for field: `%c`", b[13])
|
|
||||||
}
|
|
||||||
|
|
||||||
min, err := parseByte2Digits(b[14], b[15])
|
|
||||||
if err != nil {
|
|
||||||
return time.Time{}, err
|
|
||||||
}
|
|
||||||
if b[16] != ':' {
|
|
||||||
return time.Time{}, fmt.Errorf("bad value for field: `%c`", b[16])
|
|
||||||
}
|
|
||||||
|
|
||||||
sec, err := parseByte2Digits(b[17], b[18])
|
|
||||||
if err != nil {
|
|
||||||
return time.Time{}, err
|
|
||||||
}
|
|
||||||
if len(b) == 19 {
|
|
||||||
return time.Date(year, month, day, hour, min, sec, 0, loc), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if b[19] != '.' {
|
|
||||||
return time.Time{}, fmt.Errorf("bad value for field: `%c`", b[19])
|
|
||||||
}
|
|
||||||
nsec, err := parseByteNanoSec(b[20:])
|
|
||||||
if err != nil {
|
|
||||||
return time.Time{}, err
|
|
||||||
}
|
|
||||||
return time.Date(year, month, day, hour, min, sec, nsec, loc), nil
|
|
||||||
default:
|
|
||||||
return time.Time{}, fmt.Errorf("invalid time bytes: %s", b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseByteYear(b []byte) (int, error) {
|
|
||||||
year, n := 0, 1000
|
|
||||||
for i := 0; i < 4; i++ {
|
|
||||||
v, err := bToi(b[i])
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
year += v * n
|
|
||||||
n /= 10
|
|
||||||
}
|
|
||||||
return year, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseByte2Digits(b1, b2 byte) (int, error) {
|
|
||||||
d1, err := bToi(b1)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
d2, err := bToi(b2)
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
return d1*10 + d2, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseByteNanoSec(b []byte) (int, error) {
|
|
||||||
ns, digit := 0, 100000 // max is 6-digits
|
|
||||||
for i := 0; i < len(b); i++ {
|
|
||||||
v, err := bToi(b[i])
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
ns += v * digit
|
|
||||||
digit /= 10
|
|
||||||
}
|
|
||||||
// nanoseconds has 10-digits. (needs to scale digits)
|
|
||||||
// 10 - 6 = 4, so we have to multiple 1000.
|
|
||||||
return ns * 1000, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func bToi(b byte) (int, error) {
|
|
||||||
if b < '0' || b > '9' {
|
|
||||||
return 0, errors.New("not [0-9]")
|
|
||||||
}
|
|
||||||
return int(b - '0'), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseBinaryDateTime(num uint64, data []byte, loc *time.Location) (driver.Value, error) {
|
|
||||||
switch num {
|
|
||||||
case 0:
|
|
||||||
return time.Time{}, nil
|
|
||||||
case 4:
|
|
||||||
return time.Date(
|
|
||||||
int(binary.LittleEndian.Uint16(data[:2])), // year
|
|
||||||
time.Month(data[2]), // month
|
|
||||||
int(data[3]), // day
|
|
||||||
0, 0, 0, 0,
|
|
||||||
loc,
|
|
||||||
), nil
|
|
||||||
case 7:
|
|
||||||
return time.Date(
|
|
||||||
int(binary.LittleEndian.Uint16(data[:2])), // year
|
|
||||||
time.Month(data[2]), // month
|
|
||||||
int(data[3]), // day
|
|
||||||
int(data[4]), // hour
|
|
||||||
int(data[5]), // minutes
|
|
||||||
int(data[6]), // seconds
|
|
||||||
0,
|
|
||||||
loc,
|
|
||||||
), nil
|
|
||||||
case 11:
|
|
||||||
return time.Date(
|
|
||||||
int(binary.LittleEndian.Uint16(data[:2])), // year
|
|
||||||
time.Month(data[2]), // month
|
|
||||||
int(data[3]), // day
|
|
||||||
int(data[4]), // hour
|
|
||||||
int(data[5]), // minutes
|
|
||||||
int(data[6]), // seconds
|
|
||||||
int(binary.LittleEndian.Uint32(data[7:11]))*1000, // nanoseconds
|
|
||||||
loc,
|
|
||||||
), nil
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("invalid DATETIME packet length %d", num)
|
|
||||||
}
|
|
||||||
|
|
||||||
func appendDateTime(buf []byte, t time.Time) ([]byte, error) {
|
|
||||||
year, month, day := t.Date()
|
|
||||||
hour, min, sec := t.Clock()
|
|
||||||
nsec := t.Nanosecond()
|
|
||||||
|
|
||||||
if year < 1 || year > 9999 {
|
|
||||||
return buf, errors.New("year is not in the range [1, 9999]: " + strconv.Itoa(year)) // use errors.New instead of fmt.Errorf to avoid year escape to heap
|
|
||||||
}
|
|
||||||
year100 := year / 100
|
|
||||||
year1 := year % 100
|
|
||||||
|
|
||||||
var localBuf [len("2006-01-02T15:04:05.999999999")]byte // does not escape
|
|
||||||
localBuf[0], localBuf[1], localBuf[2], localBuf[3] = digits10[year100], digits01[year100], digits10[year1], digits01[year1]
|
|
||||||
localBuf[4] = '-'
|
|
||||||
localBuf[5], localBuf[6] = digits10[month], digits01[month]
|
|
||||||
localBuf[7] = '-'
|
|
||||||
localBuf[8], localBuf[9] = digits10[day], digits01[day]
|
|
||||||
|
|
||||||
if hour == 0 && min == 0 && sec == 0 && nsec == 0 {
|
|
||||||
return append(buf, localBuf[:10]...), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
localBuf[10] = ' '
|
|
||||||
localBuf[11], localBuf[12] = digits10[hour], digits01[hour]
|
|
||||||
localBuf[13] = ':'
|
|
||||||
localBuf[14], localBuf[15] = digits10[min], digits01[min]
|
|
||||||
localBuf[16] = ':'
|
|
||||||
localBuf[17], localBuf[18] = digits10[sec], digits01[sec]
|
|
||||||
|
|
||||||
if nsec == 0 {
|
|
||||||
return append(buf, localBuf[:19]...), nil
|
|
||||||
}
|
|
||||||
nsec100000000 := nsec / 100000000
|
|
||||||
nsec1000000 := (nsec / 1000000) % 100
|
|
||||||
nsec10000 := (nsec / 10000) % 100
|
|
||||||
nsec100 := (nsec / 100) % 100
|
|
||||||
nsec1 := nsec % 100
|
|
||||||
localBuf[19] = '.'
|
|
||||||
|
|
||||||
// milli second
|
|
||||||
localBuf[20], localBuf[21], localBuf[22] =
|
|
||||||
digits01[nsec100000000], digits10[nsec1000000], digits01[nsec1000000]
|
|
||||||
// micro second
|
|
||||||
localBuf[23], localBuf[24], localBuf[25] =
|
|
||||||
digits10[nsec10000], digits01[nsec10000], digits10[nsec100]
|
|
||||||
// nano second
|
|
||||||
localBuf[26], localBuf[27], localBuf[28] =
|
|
||||||
digits01[nsec100], digits10[nsec1], digits01[nsec1]
|
|
||||||
|
|
||||||
// trim trailing zeros
|
|
||||||
n := len(localBuf)
|
|
||||||
for n > 0 && localBuf[n-1] == '0' {
|
|
||||||
n--
|
|
||||||
}
|
|
||||||
|
|
||||||
return append(buf, localBuf[:n]...), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// zeroDateTime is used in formatBinaryDateTime to avoid an allocation
|
|
||||||
// if the DATE or DATETIME has the zero value.
|
|
||||||
// It must never be changed.
|
|
||||||
// The current behavior depends on database/sql copying the result.
|
|
||||||
var zeroDateTime = []byte("0000-00-00 00:00:00.000000")
|
|
||||||
|
|
||||||
const digits01 = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
|
|
||||||
const digits10 = "0000000000111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999"
|
|
||||||
|
|
||||||
func appendMicrosecs(dst, src []byte, decimals int) []byte {
|
|
||||||
if decimals <= 0 {
|
|
||||||
return dst
|
|
||||||
}
|
|
||||||
if len(src) == 0 {
|
|
||||||
return append(dst, ".000000"[:decimals+1]...)
|
|
||||||
}
|
|
||||||
|
|
||||||
microsecs := binary.LittleEndian.Uint32(src[:4])
|
|
||||||
p1 := byte(microsecs / 10000)
|
|
||||||
microsecs -= 10000 * uint32(p1)
|
|
||||||
p2 := byte(microsecs / 100)
|
|
||||||
microsecs -= 100 * uint32(p2)
|
|
||||||
p3 := byte(microsecs)
|
|
||||||
|
|
||||||
switch decimals {
|
|
||||||
default:
|
|
||||||
return append(dst, '.',
|
|
||||||
digits10[p1], digits01[p1],
|
|
||||||
digits10[p2], digits01[p2],
|
|
||||||
digits10[p3], digits01[p3],
|
|
||||||
)
|
|
||||||
case 1:
|
|
||||||
return append(dst, '.',
|
|
||||||
digits10[p1],
|
|
||||||
)
|
|
||||||
case 2:
|
|
||||||
return append(dst, '.',
|
|
||||||
digits10[p1], digits01[p1],
|
|
||||||
)
|
|
||||||
case 3:
|
|
||||||
return append(dst, '.',
|
|
||||||
digits10[p1], digits01[p1],
|
|
||||||
digits10[p2],
|
|
||||||
)
|
|
||||||
case 4:
|
|
||||||
return append(dst, '.',
|
|
||||||
digits10[p1], digits01[p1],
|
|
||||||
digits10[p2], digits01[p2],
|
|
||||||
)
|
|
||||||
case 5:
|
|
||||||
return append(dst, '.',
|
|
||||||
digits10[p1], digits01[p1],
|
|
||||||
digits10[p2], digits01[p2],
|
|
||||||
digits10[p3],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func formatBinaryDateTime(src []byte, length uint8) (driver.Value, error) {
|
|
||||||
// length expects the deterministic length of the zero value,
|
|
||||||
// negative time and 100+ hours are automatically added if needed
|
|
||||||
if len(src) == 0 {
|
|
||||||
return zeroDateTime[:length], nil
|
|
||||||
}
|
|
||||||
var dst []byte // return value
|
|
||||||
var p1, p2, p3 byte // current digit pair
|
|
||||||
|
|
||||||
switch length {
|
|
||||||
case 10, 19, 21, 22, 23, 24, 25, 26:
|
|
||||||
default:
|
|
||||||
t := "DATE"
|
|
||||||
if length > 10 {
|
|
||||||
t += "TIME"
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("illegal %s length %d", t, length)
|
|
||||||
}
|
|
||||||
switch len(src) {
|
|
||||||
case 4, 7, 11:
|
|
||||||
default:
|
|
||||||
t := "DATE"
|
|
||||||
if length > 10 {
|
|
||||||
t += "TIME"
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("illegal %s packet length %d", t, len(src))
|
|
||||||
}
|
|
||||||
dst = make([]byte, 0, length)
|
|
||||||
// start with the date
|
|
||||||
year := binary.LittleEndian.Uint16(src[:2])
|
|
||||||
pt := year / 100
|
|
||||||
p1 = byte(year - 100*uint16(pt))
|
|
||||||
p2, p3 = src[2], src[3]
|
|
||||||
dst = append(dst,
|
|
||||||
digits10[pt], digits01[pt],
|
|
||||||
digits10[p1], digits01[p1], '-',
|
|
||||||
digits10[p2], digits01[p2], '-',
|
|
||||||
digits10[p3], digits01[p3],
|
|
||||||
)
|
|
||||||
if length == 10 {
|
|
||||||
return dst, nil
|
|
||||||
}
|
|
||||||
if len(src) == 4 {
|
|
||||||
return append(dst, zeroDateTime[10:length]...), nil
|
|
||||||
}
|
|
||||||
dst = append(dst, ' ')
|
|
||||||
p1 = src[4] // hour
|
|
||||||
src = src[5:]
|
|
||||||
|
|
||||||
// p1 is 2-digit hour, src is after hour
|
|
||||||
p2, p3 = src[0], src[1]
|
|
||||||
dst = append(dst,
|
|
||||||
digits10[p1], digits01[p1], ':',
|
|
||||||
digits10[p2], digits01[p2], ':',
|
|
||||||
digits10[p3], digits01[p3],
|
|
||||||
)
|
|
||||||
return appendMicrosecs(dst, src[2:], int(length)-20), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func formatBinaryTime(src []byte, length uint8) (driver.Value, error) {
|
|
||||||
// length expects the deterministic length of the zero value,
|
|
||||||
// negative time and 100+ hours are automatically added if needed
|
|
||||||
if len(src) == 0 {
|
|
||||||
return zeroDateTime[11 : 11+length], nil
|
|
||||||
}
|
|
||||||
var dst []byte // return value
|
|
||||||
|
|
||||||
switch length {
|
|
||||||
case
|
|
||||||
8, // time (can be up to 10 when negative and 100+ hours)
|
|
||||||
10, 11, 12, 13, 14, 15: // time with fractional seconds
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("illegal TIME length %d", length)
|
|
||||||
}
|
|
||||||
switch len(src) {
|
|
||||||
case 8, 12:
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("invalid TIME packet length %d", len(src))
|
|
||||||
}
|
|
||||||
// +2 to enable negative time and 100+ hours
|
|
||||||
dst = make([]byte, 0, length+2)
|
|
||||||
if src[0] == 1 {
|
|
||||||
dst = append(dst, '-')
|
|
||||||
}
|
|
||||||
days := binary.LittleEndian.Uint32(src[1:5])
|
|
||||||
hours := int64(days)*24 + int64(src[5])
|
|
||||||
|
|
||||||
if hours >= 100 {
|
|
||||||
dst = strconv.AppendInt(dst, hours, 10)
|
|
||||||
} else {
|
|
||||||
dst = append(dst, digits10[hours], digits01[hours])
|
|
||||||
}
|
|
||||||
|
|
||||||
min, sec := src[6], src[7]
|
|
||||||
dst = append(dst, ':',
|
|
||||||
digits10[min], digits01[min], ':',
|
|
||||||
digits10[sec], digits01[sec],
|
|
||||||
)
|
|
||||||
return appendMicrosecs(dst, src[8:], int(length)-9), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************************************************************
|
|
||||||
* Convert from and to bytes *
|
|
||||||
******************************************************************************/
|
|
||||||
|
|
||||||
func uint64ToBytes(n uint64) []byte {
|
|
||||||
return []byte{
|
|
||||||
byte(n),
|
|
||||||
byte(n >> 8),
|
|
||||||
byte(n >> 16),
|
|
||||||
byte(n >> 24),
|
|
||||||
byte(n >> 32),
|
|
||||||
byte(n >> 40),
|
|
||||||
byte(n >> 48),
|
|
||||||
byte(n >> 56),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func uint64ToString(n uint64) []byte {
|
|
||||||
var a [20]byte
|
|
||||||
i := 20
|
|
||||||
|
|
||||||
// U+0030 = 0
|
|
||||||
// ...
|
|
||||||
// U+0039 = 9
|
|
||||||
|
|
||||||
var q uint64
|
|
||||||
for n >= 10 {
|
|
||||||
i--
|
|
||||||
q = n / 10
|
|
||||||
a[i] = uint8(n-q*10) + 0x30
|
|
||||||
n = q
|
|
||||||
}
|
|
||||||
|
|
||||||
i--
|
|
||||||
a[i] = uint8(n) + 0x30
|
|
||||||
|
|
||||||
return a[i:]
|
|
||||||
}
|
|
||||||
|
|
||||||
// treats string value as unsigned integer representation
|
|
||||||
func stringToInt(b []byte) int {
|
|
||||||
val := 0
|
|
||||||
for i := range b {
|
|
||||||
val *= 10
|
|
||||||
val += int(b[i] - 0x30)
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns the string read as a bytes slice, whether the value is NULL,
|
|
||||||
// the number of bytes read and an error, in case the string is longer than
|
|
||||||
// the input slice
|
|
||||||
func readLengthEncodedString(b []byte) ([]byte, bool, int, error) {
|
|
||||||
// Get length
|
|
||||||
num, isNull, n := readLengthEncodedInteger(b)
|
|
||||||
if num < 1 {
|
|
||||||
return b[n:n], isNull, n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
n += int(num)
|
|
||||||
|
|
||||||
// Check data length
|
|
||||||
if len(b) >= n {
|
|
||||||
return b[n-int(num) : n : n], false, n, nil
|
|
||||||
}
|
|
||||||
return nil, false, n, io.EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns the number of bytes skipped and an error, in case the string is
|
|
||||||
// longer than the input slice
|
|
||||||
func skipLengthEncodedString(b []byte) (int, error) {
|
|
||||||
// Get length
|
|
||||||
num, _, n := readLengthEncodedInteger(b)
|
|
||||||
if num < 1 {
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
n += int(num)
|
|
||||||
|
|
||||||
// Check data length
|
|
||||||
if len(b) >= n {
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
return n, io.EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns the number read, whether the value is NULL and the number of bytes read
|
|
||||||
func readLengthEncodedInteger(b []byte) (uint64, bool, int) {
|
|
||||||
// See issue #349
|
|
||||||
if len(b) == 0 {
|
|
||||||
return 0, true, 1
|
|
||||||
}
|
|
||||||
|
|
||||||
switch b[0] {
|
|
||||||
// 251: NULL
|
|
||||||
case 0xfb:
|
|
||||||
return 0, true, 1
|
|
||||||
|
|
||||||
// 252: value of following 2
|
|
||||||
case 0xfc:
|
|
||||||
return uint64(b[1]) | uint64(b[2])<<8, false, 3
|
|
||||||
|
|
||||||
// 253: value of following 3
|
|
||||||
case 0xfd:
|
|
||||||
return uint64(b[1]) | uint64(b[2])<<8 | uint64(b[3])<<16, false, 4
|
|
||||||
|
|
||||||
// 254: value of following 8
|
|
||||||
case 0xfe:
|
|
||||||
return uint64(b[1]) | uint64(b[2])<<8 | uint64(b[3])<<16 |
|
|
||||||
uint64(b[4])<<24 | uint64(b[5])<<32 | uint64(b[6])<<40 |
|
|
||||||
uint64(b[7])<<48 | uint64(b[8])<<56,
|
|
||||||
false, 9
|
|
||||||
}
|
|
||||||
|
|
||||||
// 0-250: value of first byte
|
|
||||||
return uint64(b[0]), false, 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// encodes a uint64 value and appends it to the given bytes slice
|
|
||||||
func appendLengthEncodedInteger(b []byte, n uint64) []byte {
|
|
||||||
switch {
|
|
||||||
case n <= 250:
|
|
||||||
return append(b, byte(n))
|
|
||||||
|
|
||||||
case n <= 0xffff:
|
|
||||||
return append(b, 0xfc, byte(n), byte(n>>8))
|
|
||||||
|
|
||||||
case n <= 0xffffff:
|
|
||||||
return append(b, 0xfd, byte(n), byte(n>>8), byte(n>>16))
|
|
||||||
}
|
|
||||||
return append(b, 0xfe, byte(n), byte(n>>8), byte(n>>16), byte(n>>24),
|
|
||||||
byte(n>>32), byte(n>>40), byte(n>>48), byte(n>>56))
|
|
||||||
}
|
|
||||||
|
|
||||||
// reserveBuffer checks cap(buf) and expand buffer to len(buf) + appendSize.
|
|
||||||
// If cap(buf) is not enough, reallocate new buffer.
|
|
||||||
func reserveBuffer(buf []byte, appendSize int) []byte {
|
|
||||||
newSize := len(buf) + appendSize
|
|
||||||
if cap(buf) < newSize {
|
|
||||||
// Grow buffer exponentially
|
|
||||||
newBuf := make([]byte, len(buf)*2+appendSize)
|
|
||||||
copy(newBuf, buf)
|
|
||||||
buf = newBuf
|
|
||||||
}
|
|
||||||
return buf[:newSize]
|
|
||||||
}
|
|
||||||
|
|
||||||
// escapeBytesBackslash escapes []byte with backslashes (\)
|
|
||||||
// This escapes the contents of a string (provided as []byte) by adding backslashes before special
|
|
||||||
// characters, and turning others into specific escape sequences, such as
|
|
||||||
// turning newlines into \n and null bytes into \0.
|
|
||||||
// https://github.com/mysql/mysql-server/blob/mysql-5.7.5/mysys/charset.c#L823-L932
|
|
||||||
func escapeBytesBackslash(buf, v []byte) []byte {
|
|
||||||
pos := len(buf)
|
|
||||||
buf = reserveBuffer(buf, len(v)*2)
|
|
||||||
|
|
||||||
for _, c := range v {
|
|
||||||
switch c {
|
|
||||||
case '\x00':
|
|
||||||
buf[pos+1] = '0'
|
|
||||||
buf[pos] = '\\'
|
|
||||||
pos += 2
|
|
||||||
case '\n':
|
|
||||||
buf[pos+1] = 'n'
|
|
||||||
buf[pos] = '\\'
|
|
||||||
pos += 2
|
|
||||||
case '\r':
|
|
||||||
buf[pos+1] = 'r'
|
|
||||||
buf[pos] = '\\'
|
|
||||||
pos += 2
|
|
||||||
case '\x1a':
|
|
||||||
buf[pos+1] = 'Z'
|
|
||||||
buf[pos] = '\\'
|
|
||||||
pos += 2
|
|
||||||
case '\'':
|
|
||||||
buf[pos+1] = '\''
|
|
||||||
buf[pos] = '\\'
|
|
||||||
pos += 2
|
|
||||||
case '"':
|
|
||||||
buf[pos+1] = '"'
|
|
||||||
buf[pos] = '\\'
|
|
||||||
pos += 2
|
|
||||||
case '\\':
|
|
||||||
buf[pos+1] = '\\'
|
|
||||||
buf[pos] = '\\'
|
|
||||||
pos += 2
|
|
||||||
default:
|
|
||||||
buf[pos] = c
|
|
||||||
pos++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf[:pos]
|
|
||||||
}
|
|
||||||
|
|
||||||
// escapeStringBackslash is similar to escapeBytesBackslash but for string.
|
|
||||||
func escapeStringBackslash(buf []byte, v string) []byte {
|
|
||||||
pos := len(buf)
|
|
||||||
buf = reserveBuffer(buf, len(v)*2)
|
|
||||||
|
|
||||||
for i := 0; i < len(v); i++ {
|
|
||||||
c := v[i]
|
|
||||||
switch c {
|
|
||||||
case '\x00':
|
|
||||||
buf[pos+1] = '0'
|
|
||||||
buf[pos] = '\\'
|
|
||||||
pos += 2
|
|
||||||
case '\n':
|
|
||||||
buf[pos+1] = 'n'
|
|
||||||
buf[pos] = '\\'
|
|
||||||
pos += 2
|
|
||||||
case '\r':
|
|
||||||
buf[pos+1] = 'r'
|
|
||||||
buf[pos] = '\\'
|
|
||||||
pos += 2
|
|
||||||
case '\x1a':
|
|
||||||
buf[pos+1] = 'Z'
|
|
||||||
buf[pos] = '\\'
|
|
||||||
pos += 2
|
|
||||||
case '\'':
|
|
||||||
buf[pos+1] = '\''
|
|
||||||
buf[pos] = '\\'
|
|
||||||
pos += 2
|
|
||||||
case '"':
|
|
||||||
buf[pos+1] = '"'
|
|
||||||
buf[pos] = '\\'
|
|
||||||
pos += 2
|
|
||||||
case '\\':
|
|
||||||
buf[pos+1] = '\\'
|
|
||||||
buf[pos] = '\\'
|
|
||||||
pos += 2
|
|
||||||
default:
|
|
||||||
buf[pos] = c
|
|
||||||
pos++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf[:pos]
|
|
||||||
}
|
|
||||||
|
|
||||||
// escapeBytesQuotes escapes apostrophes in []byte by doubling them up.
|
|
||||||
// This escapes the contents of a string by doubling up any apostrophes that
|
|
||||||
// it contains. This is used when the NO_BACKSLASH_ESCAPES SQL_MODE is in
|
|
||||||
// effect on the server.
|
|
||||||
// https://github.com/mysql/mysql-server/blob/mysql-5.7.5/mysys/charset.c#L963-L1038
|
|
||||||
func escapeBytesQuotes(buf, v []byte) []byte {
|
|
||||||
pos := len(buf)
|
|
||||||
buf = reserveBuffer(buf, len(v)*2)
|
|
||||||
|
|
||||||
for _, c := range v {
|
|
||||||
if c == '\'' {
|
|
||||||
buf[pos+1] = '\''
|
|
||||||
buf[pos] = '\''
|
|
||||||
pos += 2
|
|
||||||
} else {
|
|
||||||
buf[pos] = c
|
|
||||||
pos++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf[:pos]
|
|
||||||
}
|
|
||||||
|
|
||||||
// escapeStringQuotes is similar to escapeBytesQuotes but for string.
|
|
||||||
func escapeStringQuotes(buf []byte, v string) []byte {
|
|
||||||
pos := len(buf)
|
|
||||||
buf = reserveBuffer(buf, len(v)*2)
|
|
||||||
|
|
||||||
for i := 0; i < len(v); i++ {
|
|
||||||
c := v[i]
|
|
||||||
if c == '\'' {
|
|
||||||
buf[pos+1] = '\''
|
|
||||||
buf[pos] = '\''
|
|
||||||
pos += 2
|
|
||||||
} else {
|
|
||||||
buf[pos] = c
|
|
||||||
pos++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf[:pos]
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************************************************************
|
|
||||||
* Sync utils *
|
|
||||||
******************************************************************************/
|
|
||||||
|
|
||||||
// noCopy may be embedded into structs which must not be copied
|
|
||||||
// after the first use.
|
|
||||||
//
|
|
||||||
// See https://github.com/golang/go/issues/8005#issuecomment-190753527
|
|
||||||
// for details.
|
|
||||||
type noCopy struct{}
|
|
||||||
|
|
||||||
// Lock is a no-op used by -copylocks checker from `go vet`.
|
|
||||||
func (*noCopy) Lock() {}
|
|
||||||
|
|
||||||
// Unlock is a no-op used by -copylocks checker from `go vet`.
|
|
||||||
// noCopy should implement sync.Locker from Go 1.11
|
|
||||||
// https://github.com/golang/go/commit/c2eba53e7f80df21d51285879d51ab81bcfbf6bc
|
|
||||||
// https://github.com/golang/go/issues/26165
|
|
||||||
func (*noCopy) Unlock() {}
|
|
||||||
|
|
||||||
// atomicError is a wrapper for atomically accessed error values
|
|
||||||
type atomicError struct {
|
|
||||||
_ noCopy
|
|
||||||
value atomic.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set sets the error value regardless of the previous value.
|
|
||||||
// The value must not be nil
|
|
||||||
func (ae *atomicError) Set(value error) {
|
|
||||||
ae.value.Store(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Value returns the current error value
|
|
||||||
func (ae *atomicError) Value() error {
|
|
||||||
if v := ae.value.Load(); v != nil {
|
|
||||||
// this will panic if the value doesn't implement the error interface
|
|
||||||
return v.(error)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func namedValueToValue(named []driver.NamedValue) ([]driver.Value, error) {
|
|
||||||
dargs := make([]driver.Value, len(named))
|
|
||||||
for n, param := range named {
|
|
||||||
if len(param.Name) > 0 {
|
|
||||||
// TODO: support the use of Named Parameters #561
|
|
||||||
return nil, errors.New("mysql: driver does not support the use of Named Parameters")
|
|
||||||
}
|
|
||||||
dargs[n] = param.Value
|
|
||||||
}
|
|
||||||
return dargs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func mapIsolationLevel(level driver.IsolationLevel) (string, error) {
|
|
||||||
switch sql.IsolationLevel(level) {
|
|
||||||
case sql.LevelRepeatableRead:
|
|
||||||
return "REPEATABLE READ", nil
|
|
||||||
case sql.LevelReadCommitted:
|
|
||||||
return "READ COMMITTED", nil
|
|
||||||
case sql.LevelReadUncommitted:
|
|
||||||
return "READ UNCOMMITTED", nil
|
|
||||||
case sql.LevelSerializable:
|
|
||||||
return "SERIALIZABLE", nil
|
|
||||||
default:
|
|
||||||
return "", fmt.Errorf("mysql: unsupported isolation level: %v", level)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
304
vendor/github.com/klauspost/compress/LICENSE
generated
vendored
Normal file
304
vendor/github.com/klauspost/compress/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,304 @@
|
|||||||
|
Copyright (c) 2012 The Go Authors. All rights reserved.
|
||||||
|
Copyright (c) 2019 Klaus Post. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Files: gzhttp/*
|
||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright 2016-2017 The New York Times Company
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
------------------
|
||||||
|
|
||||||
|
Files: s2/cmd/internal/readahead/*
|
||||||
|
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015 Klaus Post
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
---------------------
|
||||||
|
Files: snappy/*
|
||||||
|
Files: internal/snapref/*
|
||||||
|
|
||||||
|
Copyright (c) 2011 The Snappy-Go Authors. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
Files: s2/cmd/internal/filepathx/*
|
||||||
|
|
||||||
|
Copyright 2016 The filepathx Authors
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
989
vendor/github.com/klauspost/compress/flate/deflate.go
generated
vendored
Normal file
989
vendor/github.com/klauspost/compress/flate/deflate.go
generated
vendored
Normal file
@@ -0,0 +1,989 @@
|
|||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Copyright (c) 2015 Klaus Post
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package flate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
NoCompression = 0
|
||||||
|
BestSpeed = 1
|
||||||
|
BestCompression = 9
|
||||||
|
DefaultCompression = -1
|
||||||
|
|
||||||
|
// HuffmanOnly disables Lempel-Ziv match searching and only performs Huffman
|
||||||
|
// entropy encoding. This mode is useful in compressing data that has
|
||||||
|
// already been compressed with an LZ style algorithm (e.g. Snappy or LZ4)
|
||||||
|
// that lacks an entropy encoder. Compression gains are achieved when
|
||||||
|
// certain bytes in the input stream occur more frequently than others.
|
||||||
|
//
|
||||||
|
// Note that HuffmanOnly produces a compressed output that is
|
||||||
|
// RFC 1951 compliant. That is, any valid DEFLATE decompressor will
|
||||||
|
// continue to be able to decompress this output.
|
||||||
|
HuffmanOnly = -2
|
||||||
|
ConstantCompression = HuffmanOnly // compatibility alias.
|
||||||
|
|
||||||
|
logWindowSize = 15
|
||||||
|
windowSize = 1 << logWindowSize
|
||||||
|
windowMask = windowSize - 1
|
||||||
|
logMaxOffsetSize = 15 // Standard DEFLATE
|
||||||
|
minMatchLength = 4 // The smallest match that the compressor looks for
|
||||||
|
maxMatchLength = 258 // The longest match for the compressor
|
||||||
|
minOffsetSize = 1 // The shortest offset that makes any sense
|
||||||
|
|
||||||
|
// The maximum number of tokens we will encode at the time.
|
||||||
|
// Smaller sizes usually creates less optimal blocks.
|
||||||
|
// Bigger can make context switching slow.
|
||||||
|
// We use this for levels 7-9, so we make it big.
|
||||||
|
maxFlateBlockTokens = 1 << 15
|
||||||
|
maxStoreBlockSize = 65535
|
||||||
|
hashBits = 17 // After 17 performance degrades
|
||||||
|
hashSize = 1 << hashBits
|
||||||
|
hashMask = (1 << hashBits) - 1
|
||||||
|
hashShift = (hashBits + minMatchLength - 1) / minMatchLength
|
||||||
|
maxHashOffset = 1 << 28
|
||||||
|
|
||||||
|
skipNever = math.MaxInt32
|
||||||
|
|
||||||
|
debugDeflate = false
|
||||||
|
)
|
||||||
|
|
||||||
|
type compressionLevel struct {
|
||||||
|
good, lazy, nice, chain, fastSkipHashing, level int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compression levels have been rebalanced from zlib deflate defaults
|
||||||
|
// to give a bigger spread in speed and compression.
|
||||||
|
// See https://blog.klauspost.com/rebalancing-deflate-compression-levels/
|
||||||
|
var levels = []compressionLevel{
|
||||||
|
{}, // 0
|
||||||
|
// Level 1-6 uses specialized algorithm - values not used
|
||||||
|
{0, 0, 0, 0, 0, 1},
|
||||||
|
{0, 0, 0, 0, 0, 2},
|
||||||
|
{0, 0, 0, 0, 0, 3},
|
||||||
|
{0, 0, 0, 0, 0, 4},
|
||||||
|
{0, 0, 0, 0, 0, 5},
|
||||||
|
{0, 0, 0, 0, 0, 6},
|
||||||
|
// Levels 7-9 use increasingly more lazy matching
|
||||||
|
// and increasingly stringent conditions for "good enough".
|
||||||
|
{8, 12, 16, 24, skipNever, 7},
|
||||||
|
{16, 30, 40, 64, skipNever, 8},
|
||||||
|
{32, 258, 258, 1024, skipNever, 9},
|
||||||
|
}
|
||||||
|
|
||||||
|
// advancedState contains state for the advanced levels, with bigger hash tables, etc.
|
||||||
|
type advancedState struct {
|
||||||
|
// deflate state
|
||||||
|
length int
|
||||||
|
offset int
|
||||||
|
maxInsertIndex int
|
||||||
|
chainHead int
|
||||||
|
hashOffset int
|
||||||
|
|
||||||
|
ii uint16 // position of last match, intended to overflow to reset.
|
||||||
|
|
||||||
|
// input window: unprocessed data is window[index:windowEnd]
|
||||||
|
index int
|
||||||
|
estBitsPerByte int
|
||||||
|
hashMatch [maxMatchLength + minMatchLength]uint32
|
||||||
|
|
||||||
|
// Input hash chains
|
||||||
|
// hashHead[hashValue] contains the largest inputIndex with the specified hash value
|
||||||
|
// If hashHead[hashValue] is within the current window, then
|
||||||
|
// hashPrev[hashHead[hashValue] & windowMask] contains the previous index
|
||||||
|
// with the same hash value.
|
||||||
|
hashHead [hashSize]uint32
|
||||||
|
hashPrev [windowSize]uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type compressor struct {
|
||||||
|
compressionLevel
|
||||||
|
|
||||||
|
h *huffmanEncoder
|
||||||
|
w *huffmanBitWriter
|
||||||
|
|
||||||
|
// compression algorithm
|
||||||
|
fill func(*compressor, []byte) int // copy data to window
|
||||||
|
step func(*compressor) // process window
|
||||||
|
|
||||||
|
window []byte
|
||||||
|
windowEnd int
|
||||||
|
blockStart int // window index where current tokens start
|
||||||
|
err error
|
||||||
|
|
||||||
|
// queued output tokens
|
||||||
|
tokens tokens
|
||||||
|
fast fastEnc
|
||||||
|
state *advancedState
|
||||||
|
|
||||||
|
sync bool // requesting flush
|
||||||
|
byteAvailable bool // if true, still need to process window[index-1].
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *compressor) fillDeflate(b []byte) int {
|
||||||
|
s := d.state
|
||||||
|
if s.index >= 2*windowSize-(minMatchLength+maxMatchLength) {
|
||||||
|
// shift the window by windowSize
|
||||||
|
//copy(d.window[:], d.window[windowSize:2*windowSize])
|
||||||
|
*(*[windowSize]byte)(d.window) = *(*[windowSize]byte)(d.window[windowSize:])
|
||||||
|
s.index -= windowSize
|
||||||
|
d.windowEnd -= windowSize
|
||||||
|
if d.blockStart >= windowSize {
|
||||||
|
d.blockStart -= windowSize
|
||||||
|
} else {
|
||||||
|
d.blockStart = math.MaxInt32
|
||||||
|
}
|
||||||
|
s.hashOffset += windowSize
|
||||||
|
if s.hashOffset > maxHashOffset {
|
||||||
|
delta := s.hashOffset - 1
|
||||||
|
s.hashOffset -= delta
|
||||||
|
s.chainHead -= delta
|
||||||
|
// Iterate over slices instead of arrays to avoid copying
|
||||||
|
// the entire table onto the stack (Issue #18625).
|
||||||
|
for i, v := range s.hashPrev[:] {
|
||||||
|
if int(v) > delta {
|
||||||
|
s.hashPrev[i] = uint32(int(v) - delta)
|
||||||
|
} else {
|
||||||
|
s.hashPrev[i] = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i, v := range s.hashHead[:] {
|
||||||
|
if int(v) > delta {
|
||||||
|
s.hashHead[i] = uint32(int(v) - delta)
|
||||||
|
} else {
|
||||||
|
s.hashHead[i] = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n := copy(d.window[d.windowEnd:], b)
|
||||||
|
d.windowEnd += n
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *compressor) writeBlock(tok *tokens, index int, eof bool) error {
|
||||||
|
if index > 0 || eof {
|
||||||
|
var window []byte
|
||||||
|
if d.blockStart <= index {
|
||||||
|
window = d.window[d.blockStart:index]
|
||||||
|
}
|
||||||
|
d.blockStart = index
|
||||||
|
//d.w.writeBlock(tok, eof, window)
|
||||||
|
d.w.writeBlockDynamic(tok, eof, window, d.sync)
|
||||||
|
return d.w.err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeBlockSkip writes the current block and uses the number of tokens
|
||||||
|
// to determine if the block should be stored on no matches, or
|
||||||
|
// only huffman encoded.
|
||||||
|
func (d *compressor) writeBlockSkip(tok *tokens, index int, eof bool) error {
|
||||||
|
if index > 0 || eof {
|
||||||
|
if d.blockStart <= index {
|
||||||
|
window := d.window[d.blockStart:index]
|
||||||
|
// If we removed less than a 64th of all literals
|
||||||
|
// we huffman compress the block.
|
||||||
|
if int(tok.n) > len(window)-int(tok.n>>6) {
|
||||||
|
d.w.writeBlockHuff(eof, window, d.sync)
|
||||||
|
} else {
|
||||||
|
// Write a dynamic huffman block.
|
||||||
|
d.w.writeBlockDynamic(tok, eof, window, d.sync)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
d.w.writeBlock(tok, eof, nil)
|
||||||
|
}
|
||||||
|
d.blockStart = index
|
||||||
|
return d.w.err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// fillWindow will fill the current window with the supplied
|
||||||
|
// dictionary and calculate all hashes.
|
||||||
|
// This is much faster than doing a full encode.
|
||||||
|
// Should only be used after a start/reset.
|
||||||
|
func (d *compressor) fillWindow(b []byte) {
|
||||||
|
// Do not fill window if we are in store-only or huffman mode.
|
||||||
|
if d.level <= 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if d.fast != nil {
|
||||||
|
// encode the last data, but discard the result
|
||||||
|
if len(b) > maxMatchOffset {
|
||||||
|
b = b[len(b)-maxMatchOffset:]
|
||||||
|
}
|
||||||
|
d.fast.Encode(&d.tokens, b)
|
||||||
|
d.tokens.Reset()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s := d.state
|
||||||
|
// If we are given too much, cut it.
|
||||||
|
if len(b) > windowSize {
|
||||||
|
b = b[len(b)-windowSize:]
|
||||||
|
}
|
||||||
|
// Add all to window.
|
||||||
|
n := copy(d.window[d.windowEnd:], b)
|
||||||
|
|
||||||
|
// Calculate 256 hashes at the time (more L1 cache hits)
|
||||||
|
loops := (n + 256 - minMatchLength) / 256
|
||||||
|
for j := 0; j < loops; j++ {
|
||||||
|
startindex := j * 256
|
||||||
|
end := startindex + 256 + minMatchLength - 1
|
||||||
|
if end > n {
|
||||||
|
end = n
|
||||||
|
}
|
||||||
|
tocheck := d.window[startindex:end]
|
||||||
|
dstSize := len(tocheck) - minMatchLength + 1
|
||||||
|
|
||||||
|
if dstSize <= 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
dst := s.hashMatch[:dstSize]
|
||||||
|
bulkHash4(tocheck, dst)
|
||||||
|
var newH uint32
|
||||||
|
for i, val := range dst {
|
||||||
|
di := i + startindex
|
||||||
|
newH = val & hashMask
|
||||||
|
// Get previous value with the same hash.
|
||||||
|
// Our chain should point to the previous value.
|
||||||
|
s.hashPrev[di&windowMask] = s.hashHead[newH]
|
||||||
|
// Set the head of the hash chain to us.
|
||||||
|
s.hashHead[newH] = uint32(di + s.hashOffset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Update window information.
|
||||||
|
d.windowEnd += n
|
||||||
|
s.index = n
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to find a match starting at index whose length is greater than prevSize.
|
||||||
|
// We only look at chainCount possibilities before giving up.
|
||||||
|
// pos = s.index, prevHead = s.chainHead-s.hashOffset, prevLength=minMatchLength-1, lookahead
|
||||||
|
func (d *compressor) findMatch(pos int, prevHead int, lookahead int) (length, offset int, ok bool) {
|
||||||
|
minMatchLook := maxMatchLength
|
||||||
|
if lookahead < minMatchLook {
|
||||||
|
minMatchLook = lookahead
|
||||||
|
}
|
||||||
|
|
||||||
|
win := d.window[0 : pos+minMatchLook]
|
||||||
|
|
||||||
|
// We quit when we get a match that's at least nice long
|
||||||
|
nice := len(win) - pos
|
||||||
|
if d.nice < nice {
|
||||||
|
nice = d.nice
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we've got a match that's good enough, only look in 1/4 the chain.
|
||||||
|
tries := d.chain
|
||||||
|
length = minMatchLength - 1
|
||||||
|
|
||||||
|
wEnd := win[pos+length]
|
||||||
|
wPos := win[pos:]
|
||||||
|
minIndex := pos - windowSize
|
||||||
|
if minIndex < 0 {
|
||||||
|
minIndex = 0
|
||||||
|
}
|
||||||
|
offset = 0
|
||||||
|
|
||||||
|
if d.chain < 100 {
|
||||||
|
for i := prevHead; tries > 0; tries-- {
|
||||||
|
if wEnd == win[i+length] {
|
||||||
|
n := matchLen(win[i:i+minMatchLook], wPos)
|
||||||
|
if n > length {
|
||||||
|
length = n
|
||||||
|
offset = pos - i
|
||||||
|
ok = true
|
||||||
|
if n >= nice {
|
||||||
|
// The match is good enough that we don't try to find a better one.
|
||||||
|
break
|
||||||
|
}
|
||||||
|
wEnd = win[pos+n]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if i <= minIndex {
|
||||||
|
// hashPrev[i & windowMask] has already been overwritten, so stop now.
|
||||||
|
break
|
||||||
|
}
|
||||||
|
i = int(d.state.hashPrev[i&windowMask]) - d.state.hashOffset
|
||||||
|
if i < minIndex {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Minimum gain to accept a match.
|
||||||
|
cGain := 4
|
||||||
|
|
||||||
|
// Some like it higher (CSV), some like it lower (JSON)
|
||||||
|
const baseCost = 3
|
||||||
|
// Base is 4 bytes at with an additional cost.
|
||||||
|
// Matches must be better than this.
|
||||||
|
|
||||||
|
for i := prevHead; tries > 0; tries-- {
|
||||||
|
if wEnd == win[i+length] {
|
||||||
|
n := matchLen(win[i:i+minMatchLook], wPos)
|
||||||
|
if n > length {
|
||||||
|
// Calculate gain. Estimate
|
||||||
|
newGain := d.h.bitLengthRaw(wPos[:n]) - int(offsetExtraBits[offsetCode(uint32(pos-i))]) - baseCost - int(lengthExtraBits[lengthCodes[(n-3)&255]])
|
||||||
|
|
||||||
|
//fmt.Println("gain:", newGain, "prev:", cGain, "raw:", d.h.bitLengthRaw(wPos[:n]), "this-len:", n, "prev-len:", length)
|
||||||
|
if newGain > cGain {
|
||||||
|
length = n
|
||||||
|
offset = pos - i
|
||||||
|
cGain = newGain
|
||||||
|
ok = true
|
||||||
|
if n >= nice {
|
||||||
|
// The match is good enough that we don't try to find a better one.
|
||||||
|
break
|
||||||
|
}
|
||||||
|
wEnd = win[pos+n]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if i <= minIndex {
|
||||||
|
// hashPrev[i & windowMask] has already been overwritten, so stop now.
|
||||||
|
break
|
||||||
|
}
|
||||||
|
i = int(d.state.hashPrev[i&windowMask]) - d.state.hashOffset
|
||||||
|
if i < minIndex {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *compressor) writeStoredBlock(buf []byte) error {
|
||||||
|
if d.w.writeStoredHeader(len(buf), false); d.w.err != nil {
|
||||||
|
return d.w.err
|
||||||
|
}
|
||||||
|
d.w.writeBytes(buf)
|
||||||
|
return d.w.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// hash4 returns a hash representation of the first 4 bytes
|
||||||
|
// of the supplied slice.
|
||||||
|
// The caller must ensure that len(b) >= 4.
|
||||||
|
func hash4(b []byte) uint32 {
|
||||||
|
return hash4u(binary.LittleEndian.Uint32(b), hashBits)
|
||||||
|
}
|
||||||
|
|
||||||
|
// hash4 returns the hash of u to fit in a hash table with h bits.
|
||||||
|
// Preferably h should be a constant and should always be <32.
|
||||||
|
func hash4u(u uint32, h uint8) uint32 {
|
||||||
|
return (u * prime4bytes) >> (32 - h)
|
||||||
|
}
|
||||||
|
|
||||||
|
// bulkHash4 will compute hashes using the same
|
||||||
|
// algorithm as hash4
|
||||||
|
func bulkHash4(b []byte, dst []uint32) {
|
||||||
|
if len(b) < 4 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
hb := binary.LittleEndian.Uint32(b)
|
||||||
|
|
||||||
|
dst[0] = hash4u(hb, hashBits)
|
||||||
|
end := len(b) - 4 + 1
|
||||||
|
for i := 1; i < end; i++ {
|
||||||
|
hb = (hb >> 8) | uint32(b[i+3])<<24
|
||||||
|
dst[i] = hash4u(hb, hashBits)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *compressor) initDeflate() {
|
||||||
|
d.window = make([]byte, 2*windowSize)
|
||||||
|
d.byteAvailable = false
|
||||||
|
d.err = nil
|
||||||
|
if d.state == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s := d.state
|
||||||
|
s.index = 0
|
||||||
|
s.hashOffset = 1
|
||||||
|
s.length = minMatchLength - 1
|
||||||
|
s.offset = 0
|
||||||
|
s.chainHead = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// deflateLazy is the same as deflate, but with d.fastSkipHashing == skipNever,
|
||||||
|
// meaning it always has lazy matching on.
|
||||||
|
func (d *compressor) deflateLazy() {
|
||||||
|
s := d.state
|
||||||
|
// Sanity enables additional runtime tests.
|
||||||
|
// It's intended to be used during development
|
||||||
|
// to supplement the currently ad-hoc unit tests.
|
||||||
|
const sanity = debugDeflate
|
||||||
|
|
||||||
|
if d.windowEnd-s.index < minMatchLength+maxMatchLength && !d.sync {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if d.windowEnd != s.index && d.chain > 100 {
|
||||||
|
// Get literal huffman coder.
|
||||||
|
if d.h == nil {
|
||||||
|
d.h = newHuffmanEncoder(maxFlateBlockTokens)
|
||||||
|
}
|
||||||
|
var tmp [256]uint16
|
||||||
|
for _, v := range d.window[s.index:d.windowEnd] {
|
||||||
|
tmp[v]++
|
||||||
|
}
|
||||||
|
d.h.generate(tmp[:], 15)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.maxInsertIndex = d.windowEnd - (minMatchLength - 1)
|
||||||
|
|
||||||
|
for {
|
||||||
|
if sanity && s.index > d.windowEnd {
|
||||||
|
panic("index > windowEnd")
|
||||||
|
}
|
||||||
|
lookahead := d.windowEnd - s.index
|
||||||
|
if lookahead < minMatchLength+maxMatchLength {
|
||||||
|
if !d.sync {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if sanity && s.index > d.windowEnd {
|
||||||
|
panic("index > windowEnd")
|
||||||
|
}
|
||||||
|
if lookahead == 0 {
|
||||||
|
// Flush current output block if any.
|
||||||
|
if d.byteAvailable {
|
||||||
|
// There is still one pending token that needs to be flushed
|
||||||
|
d.tokens.AddLiteral(d.window[s.index-1])
|
||||||
|
d.byteAvailable = false
|
||||||
|
}
|
||||||
|
if d.tokens.n > 0 {
|
||||||
|
if d.err = d.writeBlock(&d.tokens, s.index, false); d.err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.tokens.Reset()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if s.index < s.maxInsertIndex {
|
||||||
|
// Update the hash
|
||||||
|
hash := hash4(d.window[s.index:])
|
||||||
|
ch := s.hashHead[hash]
|
||||||
|
s.chainHead = int(ch)
|
||||||
|
s.hashPrev[s.index&windowMask] = ch
|
||||||
|
s.hashHead[hash] = uint32(s.index + s.hashOffset)
|
||||||
|
}
|
||||||
|
prevLength := s.length
|
||||||
|
prevOffset := s.offset
|
||||||
|
s.length = minMatchLength - 1
|
||||||
|
s.offset = 0
|
||||||
|
minIndex := s.index - windowSize
|
||||||
|
if minIndex < 0 {
|
||||||
|
minIndex = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.chainHead-s.hashOffset >= minIndex && lookahead > prevLength && prevLength < d.lazy {
|
||||||
|
if newLength, newOffset, ok := d.findMatch(s.index, s.chainHead-s.hashOffset, lookahead); ok {
|
||||||
|
s.length = newLength
|
||||||
|
s.offset = newOffset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if prevLength >= minMatchLength && s.length <= prevLength {
|
||||||
|
// No better match, but check for better match at end...
|
||||||
|
//
|
||||||
|
// Skip forward a number of bytes.
|
||||||
|
// Offset of 2 seems to yield best results. 3 is sometimes better.
|
||||||
|
const checkOff = 2
|
||||||
|
|
||||||
|
// Check all, except full length
|
||||||
|
if prevLength < maxMatchLength-checkOff {
|
||||||
|
prevIndex := s.index - 1
|
||||||
|
if prevIndex+prevLength < s.maxInsertIndex {
|
||||||
|
end := lookahead
|
||||||
|
if lookahead > maxMatchLength+checkOff {
|
||||||
|
end = maxMatchLength + checkOff
|
||||||
|
}
|
||||||
|
end += prevIndex
|
||||||
|
|
||||||
|
// Hash at match end.
|
||||||
|
h := hash4(d.window[prevIndex+prevLength:])
|
||||||
|
ch2 := int(s.hashHead[h]) - s.hashOffset - prevLength
|
||||||
|
if prevIndex-ch2 != prevOffset && ch2 > minIndex+checkOff {
|
||||||
|
length := matchLen(d.window[prevIndex+checkOff:end], d.window[ch2+checkOff:])
|
||||||
|
// It seems like a pure length metric is best.
|
||||||
|
if length > prevLength {
|
||||||
|
prevLength = length
|
||||||
|
prevOffset = prevIndex - ch2
|
||||||
|
|
||||||
|
// Extend back...
|
||||||
|
for i := checkOff - 1; i >= 0; i-- {
|
||||||
|
if prevLength >= maxMatchLength || d.window[prevIndex+i] != d.window[ch2+i] {
|
||||||
|
// Emit tokens we "owe"
|
||||||
|
for j := 0; j <= i; j++ {
|
||||||
|
d.tokens.AddLiteral(d.window[prevIndex+j])
|
||||||
|
if d.tokens.n == maxFlateBlockTokens {
|
||||||
|
// The block includes the current character
|
||||||
|
if d.err = d.writeBlock(&d.tokens, s.index, false); d.err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.tokens.Reset()
|
||||||
|
}
|
||||||
|
s.index++
|
||||||
|
if s.index < s.maxInsertIndex {
|
||||||
|
h := hash4(d.window[s.index:])
|
||||||
|
ch := s.hashHead[h]
|
||||||
|
s.chainHead = int(ch)
|
||||||
|
s.hashPrev[s.index&windowMask] = ch
|
||||||
|
s.hashHead[h] = uint32(s.index + s.hashOffset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
prevLength++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if false {
|
||||||
|
// Check one further ahead.
|
||||||
|
// Only rarely better, disabled for now.
|
||||||
|
prevIndex++
|
||||||
|
h := hash4(d.window[prevIndex+prevLength:])
|
||||||
|
ch2 := int(s.hashHead[h]) - s.hashOffset - prevLength
|
||||||
|
if prevIndex-ch2 != prevOffset && ch2 > minIndex+checkOff {
|
||||||
|
length := matchLen(d.window[prevIndex+checkOff:end], d.window[ch2+checkOff:])
|
||||||
|
// It seems like a pure length metric is best.
|
||||||
|
if length > prevLength+checkOff {
|
||||||
|
prevLength = length
|
||||||
|
prevOffset = prevIndex - ch2
|
||||||
|
prevIndex--
|
||||||
|
|
||||||
|
// Extend back...
|
||||||
|
for i := checkOff; i >= 0; i-- {
|
||||||
|
if prevLength >= maxMatchLength || d.window[prevIndex+i] != d.window[ch2+i-1] {
|
||||||
|
// Emit tokens we "owe"
|
||||||
|
for j := 0; j <= i; j++ {
|
||||||
|
d.tokens.AddLiteral(d.window[prevIndex+j])
|
||||||
|
if d.tokens.n == maxFlateBlockTokens {
|
||||||
|
// The block includes the current character
|
||||||
|
if d.err = d.writeBlock(&d.tokens, s.index, false); d.err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.tokens.Reset()
|
||||||
|
}
|
||||||
|
s.index++
|
||||||
|
if s.index < s.maxInsertIndex {
|
||||||
|
h := hash4(d.window[s.index:])
|
||||||
|
ch := s.hashHead[h]
|
||||||
|
s.chainHead = int(ch)
|
||||||
|
s.hashPrev[s.index&windowMask] = ch
|
||||||
|
s.hashHead[h] = uint32(s.index + s.hashOffset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
prevLength++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// There was a match at the previous step, and the current match is
|
||||||
|
// not better. Output the previous match.
|
||||||
|
d.tokens.AddMatch(uint32(prevLength-3), uint32(prevOffset-minOffsetSize))
|
||||||
|
|
||||||
|
// Insert in the hash table all strings up to the end of the match.
|
||||||
|
// index and index-1 are already inserted. If there is not enough
|
||||||
|
// lookahead, the last two strings are not inserted into the hash
|
||||||
|
// table.
|
||||||
|
newIndex := s.index + prevLength - 1
|
||||||
|
// Calculate missing hashes
|
||||||
|
end := newIndex
|
||||||
|
if end > s.maxInsertIndex {
|
||||||
|
end = s.maxInsertIndex
|
||||||
|
}
|
||||||
|
end += minMatchLength - 1
|
||||||
|
startindex := s.index + 1
|
||||||
|
if startindex > s.maxInsertIndex {
|
||||||
|
startindex = s.maxInsertIndex
|
||||||
|
}
|
||||||
|
tocheck := d.window[startindex:end]
|
||||||
|
dstSize := len(tocheck) - minMatchLength + 1
|
||||||
|
if dstSize > 0 {
|
||||||
|
dst := s.hashMatch[:dstSize]
|
||||||
|
bulkHash4(tocheck, dst)
|
||||||
|
var newH uint32
|
||||||
|
for i, val := range dst {
|
||||||
|
di := i + startindex
|
||||||
|
newH = val & hashMask
|
||||||
|
// Get previous value with the same hash.
|
||||||
|
// Our chain should point to the previous value.
|
||||||
|
s.hashPrev[di&windowMask] = s.hashHead[newH]
|
||||||
|
// Set the head of the hash chain to us.
|
||||||
|
s.hashHead[newH] = uint32(di + s.hashOffset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s.index = newIndex
|
||||||
|
d.byteAvailable = false
|
||||||
|
s.length = minMatchLength - 1
|
||||||
|
if d.tokens.n == maxFlateBlockTokens {
|
||||||
|
// The block includes the current character
|
||||||
|
if d.err = d.writeBlock(&d.tokens, s.index, false); d.err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.tokens.Reset()
|
||||||
|
}
|
||||||
|
s.ii = 0
|
||||||
|
} else {
|
||||||
|
// Reset, if we got a match this run.
|
||||||
|
if s.length >= minMatchLength {
|
||||||
|
s.ii = 0
|
||||||
|
}
|
||||||
|
// We have a byte waiting. Emit it.
|
||||||
|
if d.byteAvailable {
|
||||||
|
s.ii++
|
||||||
|
d.tokens.AddLiteral(d.window[s.index-1])
|
||||||
|
if d.tokens.n == maxFlateBlockTokens {
|
||||||
|
if d.err = d.writeBlock(&d.tokens, s.index, false); d.err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.tokens.Reset()
|
||||||
|
}
|
||||||
|
s.index++
|
||||||
|
|
||||||
|
// If we have a long run of no matches, skip additional bytes
|
||||||
|
// Resets when s.ii overflows after 64KB.
|
||||||
|
if n := int(s.ii) - d.chain; n > 0 {
|
||||||
|
n = 1 + int(n>>6)
|
||||||
|
for j := 0; j < n; j++ {
|
||||||
|
if s.index >= d.windowEnd-1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
d.tokens.AddLiteral(d.window[s.index-1])
|
||||||
|
if d.tokens.n == maxFlateBlockTokens {
|
||||||
|
if d.err = d.writeBlock(&d.tokens, s.index, false); d.err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.tokens.Reset()
|
||||||
|
}
|
||||||
|
// Index...
|
||||||
|
if s.index < s.maxInsertIndex {
|
||||||
|
h := hash4(d.window[s.index:])
|
||||||
|
ch := s.hashHead[h]
|
||||||
|
s.chainHead = int(ch)
|
||||||
|
s.hashPrev[s.index&windowMask] = ch
|
||||||
|
s.hashHead[h] = uint32(s.index + s.hashOffset)
|
||||||
|
}
|
||||||
|
s.index++
|
||||||
|
}
|
||||||
|
// Flush last byte
|
||||||
|
d.tokens.AddLiteral(d.window[s.index-1])
|
||||||
|
d.byteAvailable = false
|
||||||
|
// s.length = minMatchLength - 1 // not needed, since s.ii is reset above, so it should never be > minMatchLength
|
||||||
|
if d.tokens.n == maxFlateBlockTokens {
|
||||||
|
if d.err = d.writeBlock(&d.tokens, s.index, false); d.err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.tokens.Reset()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s.index++
|
||||||
|
d.byteAvailable = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *compressor) store() {
|
||||||
|
if d.windowEnd > 0 && (d.windowEnd == maxStoreBlockSize || d.sync) {
|
||||||
|
d.err = d.writeStoredBlock(d.window[:d.windowEnd])
|
||||||
|
d.windowEnd = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fillWindow will fill the buffer with data for huffman-only compression.
|
||||||
|
// The number of bytes copied is returned.
|
||||||
|
func (d *compressor) fillBlock(b []byte) int {
|
||||||
|
n := copy(d.window[d.windowEnd:], b)
|
||||||
|
d.windowEnd += n
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
// storeHuff will compress and store the currently added data,
|
||||||
|
// if enough has been accumulated or we at the end of the stream.
|
||||||
|
// Any error that occurred will be in d.err
|
||||||
|
func (d *compressor) storeHuff() {
|
||||||
|
if d.windowEnd < len(d.window) && !d.sync || d.windowEnd == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
d.w.writeBlockHuff(false, d.window[:d.windowEnd], d.sync)
|
||||||
|
d.err = d.w.err
|
||||||
|
d.windowEnd = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// storeFast will compress and store the currently added data,
|
||||||
|
// if enough has been accumulated or we at the end of the stream.
|
||||||
|
// Any error that occurred will be in d.err
|
||||||
|
func (d *compressor) storeFast() {
|
||||||
|
// We only compress if we have maxStoreBlockSize.
|
||||||
|
if d.windowEnd < len(d.window) {
|
||||||
|
if !d.sync {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Handle extremely small sizes.
|
||||||
|
if d.windowEnd < 128 {
|
||||||
|
if d.windowEnd == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if d.windowEnd <= 32 {
|
||||||
|
d.err = d.writeStoredBlock(d.window[:d.windowEnd])
|
||||||
|
} else {
|
||||||
|
d.w.writeBlockHuff(false, d.window[:d.windowEnd], true)
|
||||||
|
d.err = d.w.err
|
||||||
|
}
|
||||||
|
d.tokens.Reset()
|
||||||
|
d.windowEnd = 0
|
||||||
|
d.fast.Reset()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
d.fast.Encode(&d.tokens, d.window[:d.windowEnd])
|
||||||
|
// If we made zero matches, store the block as is.
|
||||||
|
if d.tokens.n == 0 {
|
||||||
|
d.err = d.writeStoredBlock(d.window[:d.windowEnd])
|
||||||
|
// If we removed less than 1/16th, huffman compress the block.
|
||||||
|
} else if int(d.tokens.n) > d.windowEnd-(d.windowEnd>>4) {
|
||||||
|
d.w.writeBlockHuff(false, d.window[:d.windowEnd], d.sync)
|
||||||
|
d.err = d.w.err
|
||||||
|
} else {
|
||||||
|
d.w.writeBlockDynamic(&d.tokens, false, d.window[:d.windowEnd], d.sync)
|
||||||
|
d.err = d.w.err
|
||||||
|
}
|
||||||
|
d.tokens.Reset()
|
||||||
|
d.windowEnd = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// write will add input byte to the stream.
|
||||||
|
// Unless an error occurs all bytes will be consumed.
|
||||||
|
func (d *compressor) write(b []byte) (n int, err error) {
|
||||||
|
if d.err != nil {
|
||||||
|
return 0, d.err
|
||||||
|
}
|
||||||
|
n = len(b)
|
||||||
|
for len(b) > 0 {
|
||||||
|
if d.windowEnd == len(d.window) || d.sync {
|
||||||
|
d.step(d)
|
||||||
|
}
|
||||||
|
b = b[d.fill(d, b):]
|
||||||
|
if d.err != nil {
|
||||||
|
return 0, d.err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n, d.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *compressor) syncFlush() error {
|
||||||
|
d.sync = true
|
||||||
|
if d.err != nil {
|
||||||
|
return d.err
|
||||||
|
}
|
||||||
|
d.step(d)
|
||||||
|
if d.err == nil {
|
||||||
|
d.w.writeStoredHeader(0, false)
|
||||||
|
d.w.flush()
|
||||||
|
d.err = d.w.err
|
||||||
|
}
|
||||||
|
d.sync = false
|
||||||
|
return d.err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *compressor) init(w io.Writer, level int) (err error) {
|
||||||
|
d.w = newHuffmanBitWriter(w)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case level == NoCompression:
|
||||||
|
d.window = make([]byte, maxStoreBlockSize)
|
||||||
|
d.fill = (*compressor).fillBlock
|
||||||
|
d.step = (*compressor).store
|
||||||
|
case level == ConstantCompression:
|
||||||
|
d.w.logNewTablePenalty = 10
|
||||||
|
d.window = make([]byte, 32<<10)
|
||||||
|
d.fill = (*compressor).fillBlock
|
||||||
|
d.step = (*compressor).storeHuff
|
||||||
|
case level == DefaultCompression:
|
||||||
|
level = 5
|
||||||
|
fallthrough
|
||||||
|
case level >= 1 && level <= 6:
|
||||||
|
d.w.logNewTablePenalty = 7
|
||||||
|
d.fast = newFastEnc(level)
|
||||||
|
d.window = make([]byte, maxStoreBlockSize)
|
||||||
|
d.fill = (*compressor).fillBlock
|
||||||
|
d.step = (*compressor).storeFast
|
||||||
|
case 7 <= level && level <= 9:
|
||||||
|
d.w.logNewTablePenalty = 8
|
||||||
|
d.state = &advancedState{}
|
||||||
|
d.compressionLevel = levels[level]
|
||||||
|
d.initDeflate()
|
||||||
|
d.fill = (*compressor).fillDeflate
|
||||||
|
d.step = (*compressor).deflateLazy
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("flate: invalid compression level %d: want value in range [-2, 9]", level)
|
||||||
|
}
|
||||||
|
d.level = level
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset the state of the compressor.
|
||||||
|
func (d *compressor) reset(w io.Writer) {
|
||||||
|
d.w.reset(w)
|
||||||
|
d.sync = false
|
||||||
|
d.err = nil
|
||||||
|
// We only need to reset a few things for Snappy.
|
||||||
|
if d.fast != nil {
|
||||||
|
d.fast.Reset()
|
||||||
|
d.windowEnd = 0
|
||||||
|
d.tokens.Reset()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch d.compressionLevel.chain {
|
||||||
|
case 0:
|
||||||
|
// level was NoCompression or ConstantCompresssion.
|
||||||
|
d.windowEnd = 0
|
||||||
|
default:
|
||||||
|
s := d.state
|
||||||
|
s.chainHead = -1
|
||||||
|
for i := range s.hashHead {
|
||||||
|
s.hashHead[i] = 0
|
||||||
|
}
|
||||||
|
for i := range s.hashPrev {
|
||||||
|
s.hashPrev[i] = 0
|
||||||
|
}
|
||||||
|
s.hashOffset = 1
|
||||||
|
s.index, d.windowEnd = 0, 0
|
||||||
|
d.blockStart, d.byteAvailable = 0, false
|
||||||
|
d.tokens.Reset()
|
||||||
|
s.length = minMatchLength - 1
|
||||||
|
s.offset = 0
|
||||||
|
s.ii = 0
|
||||||
|
s.maxInsertIndex = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *compressor) close() error {
|
||||||
|
if d.err != nil {
|
||||||
|
return d.err
|
||||||
|
}
|
||||||
|
d.sync = true
|
||||||
|
d.step(d)
|
||||||
|
if d.err != nil {
|
||||||
|
return d.err
|
||||||
|
}
|
||||||
|
if d.w.writeStoredHeader(0, true); d.w.err != nil {
|
||||||
|
return d.w.err
|
||||||
|
}
|
||||||
|
d.w.flush()
|
||||||
|
d.w.reset(nil)
|
||||||
|
return d.w.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewWriter returns a new Writer compressing data at the given level.
|
||||||
|
// Following zlib, levels range from 1 (BestSpeed) to 9 (BestCompression);
|
||||||
|
// higher levels typically run slower but compress more.
|
||||||
|
// Level 0 (NoCompression) does not attempt any compression; it only adds the
|
||||||
|
// necessary DEFLATE framing.
|
||||||
|
// Level -1 (DefaultCompression) uses the default compression level.
|
||||||
|
// Level -2 (ConstantCompression) will use Huffman compression only, giving
|
||||||
|
// a very fast compression for all types of input, but sacrificing considerable
|
||||||
|
// compression efficiency.
|
||||||
|
//
|
||||||
|
// If level is in the range [-2, 9] then the error returned will be nil.
|
||||||
|
// Otherwise the error returned will be non-nil.
|
||||||
|
func NewWriter(w io.Writer, level int) (*Writer, error) {
|
||||||
|
var dw Writer
|
||||||
|
if err := dw.d.init(w, level); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &dw, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewWriterDict is like NewWriter but initializes the new
|
||||||
|
// Writer with a preset dictionary. The returned Writer behaves
|
||||||
|
// as if the dictionary had been written to it without producing
|
||||||
|
// any compressed output. The compressed data written to w
|
||||||
|
// can only be decompressed by a Reader initialized with the
|
||||||
|
// same dictionary.
|
||||||
|
func NewWriterDict(w io.Writer, level int, dict []byte) (*Writer, error) {
|
||||||
|
zw, err := NewWriter(w, level)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
zw.d.fillWindow(dict)
|
||||||
|
zw.dict = append(zw.dict, dict...) // duplicate dictionary for Reset method.
|
||||||
|
return zw, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Writer takes data written to it and writes the compressed
|
||||||
|
// form of that data to an underlying writer (see NewWriter).
|
||||||
|
type Writer struct {
|
||||||
|
d compressor
|
||||||
|
dict []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes data to w, which will eventually write the
|
||||||
|
// compressed form of data to its underlying writer.
|
||||||
|
func (w *Writer) Write(data []byte) (n int, err error) {
|
||||||
|
return w.d.write(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush flushes any pending data to the underlying writer.
|
||||||
|
// It is useful mainly in compressed network protocols, to ensure that
|
||||||
|
// a remote reader has enough data to reconstruct a packet.
|
||||||
|
// Flush does not return until the data has been written.
|
||||||
|
// Calling Flush when there is no pending data still causes the Writer
|
||||||
|
// to emit a sync marker of at least 4 bytes.
|
||||||
|
// If the underlying writer returns an error, Flush returns that error.
|
||||||
|
//
|
||||||
|
// In the terminology of the zlib library, Flush is equivalent to Z_SYNC_FLUSH.
|
||||||
|
func (w *Writer) Flush() error {
|
||||||
|
// For more about flushing:
|
||||||
|
// http://www.bolet.org/~pornin/deflate-flush.html
|
||||||
|
return w.d.syncFlush()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close flushes and closes the writer.
|
||||||
|
func (w *Writer) Close() error {
|
||||||
|
return w.d.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset discards the writer's state and makes it equivalent to
|
||||||
|
// the result of NewWriter or NewWriterDict called with dst
|
||||||
|
// and w's level and dictionary.
|
||||||
|
func (w *Writer) Reset(dst io.Writer) {
|
||||||
|
if len(w.dict) > 0 {
|
||||||
|
// w was created with NewWriterDict
|
||||||
|
w.d.reset(dst)
|
||||||
|
if dst != nil {
|
||||||
|
w.d.fillWindow(w.dict)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// w was created with NewWriter
|
||||||
|
w.d.reset(dst)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ResetDict discards the writer's state and makes it equivalent to
|
||||||
|
// the result of NewWriter or NewWriterDict called with dst
|
||||||
|
// and w's level, but sets a specific dictionary.
|
||||||
|
func (w *Writer) ResetDict(dst io.Writer, dict []byte) {
|
||||||
|
w.dict = dict
|
||||||
|
w.d.reset(dst)
|
||||||
|
w.d.fillWindow(w.dict)
|
||||||
|
}
|
||||||
184
vendor/github.com/klauspost/compress/flate/dict_decoder.go
generated
vendored
Normal file
184
vendor/github.com/klauspost/compress/flate/dict_decoder.go
generated
vendored
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
// Copyright 2016 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package flate
|
||||||
|
|
||||||
|
// dictDecoder implements the LZ77 sliding dictionary as used in decompression.
|
||||||
|
// LZ77 decompresses data through sequences of two forms of commands:
|
||||||
|
//
|
||||||
|
// - Literal insertions: Runs of one or more symbols are inserted into the data
|
||||||
|
// stream as is. This is accomplished through the writeByte method for a
|
||||||
|
// single symbol, or combinations of writeSlice/writeMark for multiple symbols.
|
||||||
|
// Any valid stream must start with a literal insertion if no preset dictionary
|
||||||
|
// is used.
|
||||||
|
//
|
||||||
|
// - Backward copies: Runs of one or more symbols are copied from previously
|
||||||
|
// emitted data. Backward copies come as the tuple (dist, length) where dist
|
||||||
|
// determines how far back in the stream to copy from and length determines how
|
||||||
|
// many bytes to copy. Note that it is valid for the length to be greater than
|
||||||
|
// the distance. Since LZ77 uses forward copies, that situation is used to
|
||||||
|
// perform a form of run-length encoding on repeated runs of symbols.
|
||||||
|
// The writeCopy and tryWriteCopy are used to implement this command.
|
||||||
|
//
|
||||||
|
// For performance reasons, this implementation performs little to no sanity
|
||||||
|
// checks about the arguments. As such, the invariants documented for each
|
||||||
|
// method call must be respected.
|
||||||
|
type dictDecoder struct {
|
||||||
|
hist []byte // Sliding window history
|
||||||
|
|
||||||
|
// Invariant: 0 <= rdPos <= wrPos <= len(hist)
|
||||||
|
wrPos int // Current output position in buffer
|
||||||
|
rdPos int // Have emitted hist[:rdPos] already
|
||||||
|
full bool // Has a full window length been written yet?
|
||||||
|
}
|
||||||
|
|
||||||
|
// init initializes dictDecoder to have a sliding window dictionary of the given
|
||||||
|
// size. If a preset dict is provided, it will initialize the dictionary with
|
||||||
|
// the contents of dict.
|
||||||
|
func (dd *dictDecoder) init(size int, dict []byte) {
|
||||||
|
*dd = dictDecoder{hist: dd.hist}
|
||||||
|
|
||||||
|
if cap(dd.hist) < size {
|
||||||
|
dd.hist = make([]byte, size)
|
||||||
|
}
|
||||||
|
dd.hist = dd.hist[:size]
|
||||||
|
|
||||||
|
if len(dict) > len(dd.hist) {
|
||||||
|
dict = dict[len(dict)-len(dd.hist):]
|
||||||
|
}
|
||||||
|
dd.wrPos = copy(dd.hist, dict)
|
||||||
|
if dd.wrPos == len(dd.hist) {
|
||||||
|
dd.wrPos = 0
|
||||||
|
dd.full = true
|
||||||
|
}
|
||||||
|
dd.rdPos = dd.wrPos
|
||||||
|
}
|
||||||
|
|
||||||
|
// histSize reports the total amount of historical data in the dictionary.
|
||||||
|
func (dd *dictDecoder) histSize() int {
|
||||||
|
if dd.full {
|
||||||
|
return len(dd.hist)
|
||||||
|
}
|
||||||
|
return dd.wrPos
|
||||||
|
}
|
||||||
|
|
||||||
|
// availRead reports the number of bytes that can be flushed by readFlush.
|
||||||
|
func (dd *dictDecoder) availRead() int {
|
||||||
|
return dd.wrPos - dd.rdPos
|
||||||
|
}
|
||||||
|
|
||||||
|
// availWrite reports the available amount of output buffer space.
|
||||||
|
func (dd *dictDecoder) availWrite() int {
|
||||||
|
return len(dd.hist) - dd.wrPos
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeSlice returns a slice of the available buffer to write data to.
|
||||||
|
//
|
||||||
|
// This invariant will be kept: len(s) <= availWrite()
|
||||||
|
func (dd *dictDecoder) writeSlice() []byte {
|
||||||
|
return dd.hist[dd.wrPos:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeMark advances the writer pointer by cnt.
|
||||||
|
//
|
||||||
|
// This invariant must be kept: 0 <= cnt <= availWrite()
|
||||||
|
func (dd *dictDecoder) writeMark(cnt int) {
|
||||||
|
dd.wrPos += cnt
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeByte writes a single byte to the dictionary.
|
||||||
|
//
|
||||||
|
// This invariant must be kept: 0 < availWrite()
|
||||||
|
func (dd *dictDecoder) writeByte(c byte) {
|
||||||
|
dd.hist[dd.wrPos] = c
|
||||||
|
dd.wrPos++
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeCopy copies a string at a given (dist, length) to the output.
|
||||||
|
// This returns the number of bytes copied and may be less than the requested
|
||||||
|
// length if the available space in the output buffer is too small.
|
||||||
|
//
|
||||||
|
// This invariant must be kept: 0 < dist <= histSize()
|
||||||
|
func (dd *dictDecoder) writeCopy(dist, length int) int {
|
||||||
|
dstBase := dd.wrPos
|
||||||
|
dstPos := dstBase
|
||||||
|
srcPos := dstPos - dist
|
||||||
|
endPos := dstPos + length
|
||||||
|
if endPos > len(dd.hist) {
|
||||||
|
endPos = len(dd.hist)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy non-overlapping section after destination position.
|
||||||
|
//
|
||||||
|
// This section is non-overlapping in that the copy length for this section
|
||||||
|
// is always less than or equal to the backwards distance. This can occur
|
||||||
|
// if a distance refers to data that wraps-around in the buffer.
|
||||||
|
// Thus, a backwards copy is performed here; that is, the exact bytes in
|
||||||
|
// the source prior to the copy is placed in the destination.
|
||||||
|
if srcPos < 0 {
|
||||||
|
srcPos += len(dd.hist)
|
||||||
|
dstPos += copy(dd.hist[dstPos:endPos], dd.hist[srcPos:])
|
||||||
|
srcPos = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy possibly overlapping section before destination position.
|
||||||
|
//
|
||||||
|
// This section can overlap if the copy length for this section is larger
|
||||||
|
// than the backwards distance. This is allowed by LZ77 so that repeated
|
||||||
|
// strings can be succinctly represented using (dist, length) pairs.
|
||||||
|
// Thus, a forwards copy is performed here; that is, the bytes copied is
|
||||||
|
// possibly dependent on the resulting bytes in the destination as the copy
|
||||||
|
// progresses along. This is functionally equivalent to the following:
|
||||||
|
//
|
||||||
|
// for i := 0; i < endPos-dstPos; i++ {
|
||||||
|
// dd.hist[dstPos+i] = dd.hist[srcPos+i]
|
||||||
|
// }
|
||||||
|
// dstPos = endPos
|
||||||
|
//
|
||||||
|
for dstPos < endPos {
|
||||||
|
dstPos += copy(dd.hist[dstPos:endPos], dd.hist[srcPos:dstPos])
|
||||||
|
}
|
||||||
|
|
||||||
|
dd.wrPos = dstPos
|
||||||
|
return dstPos - dstBase
|
||||||
|
}
|
||||||
|
|
||||||
|
// tryWriteCopy tries to copy a string at a given (distance, length) to the
|
||||||
|
// output. This specialized version is optimized for short distances.
|
||||||
|
//
|
||||||
|
// This method is designed to be inlined for performance reasons.
|
||||||
|
//
|
||||||
|
// This invariant must be kept: 0 < dist <= histSize()
|
||||||
|
func (dd *dictDecoder) tryWriteCopy(dist, length int) int {
|
||||||
|
dstPos := dd.wrPos
|
||||||
|
endPos := dstPos + length
|
||||||
|
if dstPos < dist || endPos > len(dd.hist) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
dstBase := dstPos
|
||||||
|
srcPos := dstPos - dist
|
||||||
|
|
||||||
|
// Copy possibly overlapping section before destination position.
|
||||||
|
loop:
|
||||||
|
dstPos += copy(dd.hist[dstPos:endPos], dd.hist[srcPos:dstPos])
|
||||||
|
if dstPos < endPos {
|
||||||
|
goto loop // Avoid for-loop so that this function can be inlined
|
||||||
|
}
|
||||||
|
|
||||||
|
dd.wrPos = dstPos
|
||||||
|
return dstPos - dstBase
|
||||||
|
}
|
||||||
|
|
||||||
|
// readFlush returns a slice of the historical buffer that is ready to be
|
||||||
|
// emitted to the user. The data returned by readFlush must be fully consumed
|
||||||
|
// before calling any other dictDecoder methods.
|
||||||
|
func (dd *dictDecoder) readFlush() []byte {
|
||||||
|
toRead := dd.hist[dd.rdPos:dd.wrPos]
|
||||||
|
dd.rdPos = dd.wrPos
|
||||||
|
if dd.wrPos == len(dd.hist) {
|
||||||
|
dd.wrPos, dd.rdPos = 0, 0
|
||||||
|
dd.full = true
|
||||||
|
}
|
||||||
|
return toRead
|
||||||
|
}
|
||||||
216
vendor/github.com/klauspost/compress/flate/fast_encoder.go
generated
vendored
Normal file
216
vendor/github.com/klauspost/compress/flate/fast_encoder.go
generated
vendored
Normal file
@@ -0,0 +1,216 @@
|
|||||||
|
// Copyright 2011 The Snappy-Go Authors. All rights reserved.
|
||||||
|
// Modified for deflate by Klaus Post (c) 2015.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package flate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"math/bits"
|
||||||
|
)
|
||||||
|
|
||||||
|
type fastEnc interface {
|
||||||
|
Encode(dst *tokens, src []byte)
|
||||||
|
Reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFastEnc(level int) fastEnc {
|
||||||
|
switch level {
|
||||||
|
case 1:
|
||||||
|
return &fastEncL1{fastGen: fastGen{cur: maxStoreBlockSize}}
|
||||||
|
case 2:
|
||||||
|
return &fastEncL2{fastGen: fastGen{cur: maxStoreBlockSize}}
|
||||||
|
case 3:
|
||||||
|
return &fastEncL3{fastGen: fastGen{cur: maxStoreBlockSize}}
|
||||||
|
case 4:
|
||||||
|
return &fastEncL4{fastGen: fastGen{cur: maxStoreBlockSize}}
|
||||||
|
case 5:
|
||||||
|
return &fastEncL5{fastGen: fastGen{cur: maxStoreBlockSize}}
|
||||||
|
case 6:
|
||||||
|
return &fastEncL6{fastGen: fastGen{cur: maxStoreBlockSize}}
|
||||||
|
default:
|
||||||
|
panic("invalid level specified")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
tableBits = 15 // Bits used in the table
|
||||||
|
tableSize = 1 << tableBits // Size of the table
|
||||||
|
tableShift = 32 - tableBits // Right-shift to get the tableBits most significant bits of a uint32.
|
||||||
|
baseMatchOffset = 1 // The smallest match offset
|
||||||
|
baseMatchLength = 3 // The smallest match length per the RFC section 3.2.5
|
||||||
|
maxMatchOffset = 1 << 15 // The largest match offset
|
||||||
|
|
||||||
|
bTableBits = 17 // Bits used in the big tables
|
||||||
|
bTableSize = 1 << bTableBits // Size of the table
|
||||||
|
allocHistory = maxStoreBlockSize * 5 // Size to preallocate for history.
|
||||||
|
bufferReset = (1 << 31) - allocHistory - maxStoreBlockSize - 1 // Reset the buffer offset when reaching this.
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
prime3bytes = 506832829
|
||||||
|
prime4bytes = 2654435761
|
||||||
|
prime5bytes = 889523592379
|
||||||
|
prime6bytes = 227718039650203
|
||||||
|
prime7bytes = 58295818150454627
|
||||||
|
prime8bytes = 0xcf1bbcdcb7a56463
|
||||||
|
)
|
||||||
|
|
||||||
|
func load3232(b []byte, i int32) uint32 {
|
||||||
|
return binary.LittleEndian.Uint32(b[i:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func load6432(b []byte, i int32) uint64 {
|
||||||
|
return binary.LittleEndian.Uint64(b[i:])
|
||||||
|
}
|
||||||
|
|
||||||
|
type tableEntry struct {
|
||||||
|
offset int32
|
||||||
|
}
|
||||||
|
|
||||||
|
// fastGen maintains the table for matches,
|
||||||
|
// and the previous byte block for level 2.
|
||||||
|
// This is the generic implementation.
|
||||||
|
type fastGen struct {
|
||||||
|
hist []byte
|
||||||
|
cur int32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *fastGen) addBlock(src []byte) int32 {
|
||||||
|
// check if we have space already
|
||||||
|
if len(e.hist)+len(src) > cap(e.hist) {
|
||||||
|
if cap(e.hist) == 0 {
|
||||||
|
e.hist = make([]byte, 0, allocHistory)
|
||||||
|
} else {
|
||||||
|
if cap(e.hist) < maxMatchOffset*2 {
|
||||||
|
panic("unexpected buffer size")
|
||||||
|
}
|
||||||
|
// Move down
|
||||||
|
offset := int32(len(e.hist)) - maxMatchOffset
|
||||||
|
// copy(e.hist[0:maxMatchOffset], e.hist[offset:])
|
||||||
|
*(*[maxMatchOffset]byte)(e.hist) = *(*[maxMatchOffset]byte)(e.hist[offset:])
|
||||||
|
e.cur += offset
|
||||||
|
e.hist = e.hist[:maxMatchOffset]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s := int32(len(e.hist))
|
||||||
|
e.hist = append(e.hist, src...)
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
type tableEntryPrev struct {
|
||||||
|
Cur tableEntry
|
||||||
|
Prev tableEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
// hash7 returns the hash of the lowest 7 bytes of u to fit in a hash table with h bits.
|
||||||
|
// Preferably h should be a constant and should always be <64.
|
||||||
|
func hash7(u uint64, h uint8) uint32 {
|
||||||
|
return uint32(((u << (64 - 56)) * prime7bytes) >> ((64 - h) & reg8SizeMask64))
|
||||||
|
}
|
||||||
|
|
||||||
|
// hashLen returns a hash of the lowest mls bytes of with length output bits.
|
||||||
|
// mls must be >=3 and <=8. Any other value will return hash for 4 bytes.
|
||||||
|
// length should always be < 32.
|
||||||
|
// Preferably length and mls should be a constant for inlining.
|
||||||
|
func hashLen(u uint64, length, mls uint8) uint32 {
|
||||||
|
switch mls {
|
||||||
|
case 3:
|
||||||
|
return (uint32(u<<8) * prime3bytes) >> (32 - length)
|
||||||
|
case 5:
|
||||||
|
return uint32(((u << (64 - 40)) * prime5bytes) >> (64 - length))
|
||||||
|
case 6:
|
||||||
|
return uint32(((u << (64 - 48)) * prime6bytes) >> (64 - length))
|
||||||
|
case 7:
|
||||||
|
return uint32(((u << (64 - 56)) * prime7bytes) >> (64 - length))
|
||||||
|
case 8:
|
||||||
|
return uint32((u * prime8bytes) >> (64 - length))
|
||||||
|
default:
|
||||||
|
return (uint32(u) * prime4bytes) >> (32 - length)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// matchlen will return the match length between offsets and t in src.
|
||||||
|
// The maximum length returned is maxMatchLength - 4.
|
||||||
|
// It is assumed that s > t, that t >=0 and s < len(src).
|
||||||
|
func (e *fastGen) matchlen(s, t int32, src []byte) int32 {
|
||||||
|
if debugDecode {
|
||||||
|
if t >= s {
|
||||||
|
panic(fmt.Sprint("t >=s:", t, s))
|
||||||
|
}
|
||||||
|
if int(s) >= len(src) {
|
||||||
|
panic(fmt.Sprint("s >= len(src):", s, len(src)))
|
||||||
|
}
|
||||||
|
if t < 0 {
|
||||||
|
panic(fmt.Sprint("t < 0:", t))
|
||||||
|
}
|
||||||
|
if s-t > maxMatchOffset {
|
||||||
|
panic(fmt.Sprint(s, "-", t, "(", s-t, ") > maxMatchLength (", maxMatchOffset, ")"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s1 := int(s) + maxMatchLength - 4
|
||||||
|
if s1 > len(src) {
|
||||||
|
s1 = len(src)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extend the match to be as long as possible.
|
||||||
|
return int32(matchLen(src[s:s1], src[t:]))
|
||||||
|
}
|
||||||
|
|
||||||
|
// matchlenLong will return the match length between offsets and t in src.
|
||||||
|
// It is assumed that s > t, that t >=0 and s < len(src).
|
||||||
|
func (e *fastGen) matchlenLong(s, t int32, src []byte) int32 {
|
||||||
|
if debugDeflate {
|
||||||
|
if t >= s {
|
||||||
|
panic(fmt.Sprint("t >=s:", t, s))
|
||||||
|
}
|
||||||
|
if int(s) >= len(src) {
|
||||||
|
panic(fmt.Sprint("s >= len(src):", s, len(src)))
|
||||||
|
}
|
||||||
|
if t < 0 {
|
||||||
|
panic(fmt.Sprint("t < 0:", t))
|
||||||
|
}
|
||||||
|
if s-t > maxMatchOffset {
|
||||||
|
panic(fmt.Sprint(s, "-", t, "(", s-t, ") > maxMatchLength (", maxMatchOffset, ")"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Extend the match to be as long as possible.
|
||||||
|
return int32(matchLen(src[s:], src[t:]))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset the encoding table.
|
||||||
|
func (e *fastGen) Reset() {
|
||||||
|
if cap(e.hist) < allocHistory {
|
||||||
|
e.hist = make([]byte, 0, allocHistory)
|
||||||
|
}
|
||||||
|
// We offset current position so everything will be out of reach.
|
||||||
|
// If we are above the buffer reset it will be cleared anyway since len(hist) == 0.
|
||||||
|
if e.cur <= bufferReset {
|
||||||
|
e.cur += maxMatchOffset + int32(len(e.hist))
|
||||||
|
}
|
||||||
|
e.hist = e.hist[:0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// matchLen returns the maximum length.
|
||||||
|
// 'a' must be the shortest of the two.
|
||||||
|
func matchLen(a, b []byte) int {
|
||||||
|
var checked int
|
||||||
|
|
||||||
|
for len(a) >= 8 {
|
||||||
|
if diff := binary.LittleEndian.Uint64(a) ^ binary.LittleEndian.Uint64(b); diff != 0 {
|
||||||
|
return checked + (bits.TrailingZeros64(diff) >> 3)
|
||||||
|
}
|
||||||
|
checked += 8
|
||||||
|
a = a[8:]
|
||||||
|
b = b[8:]
|
||||||
|
}
|
||||||
|
b = b[:len(a)]
|
||||||
|
for i := range a {
|
||||||
|
if a[i] != b[i] {
|
||||||
|
return i + checked
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return len(a) + checked
|
||||||
|
}
|
||||||
1187
vendor/github.com/klauspost/compress/flate/huffman_bit_writer.go
generated
vendored
Normal file
1187
vendor/github.com/klauspost/compress/flate/huffman_bit_writer.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
417
vendor/github.com/klauspost/compress/flate/huffman_code.go
generated
vendored
Normal file
417
vendor/github.com/klauspost/compress/flate/huffman_code.go
generated
vendored
Normal file
@@ -0,0 +1,417 @@
|
|||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package flate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"math/bits"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
maxBitsLimit = 16
|
||||||
|
// number of valid literals
|
||||||
|
literalCount = 286
|
||||||
|
)
|
||||||
|
|
||||||
|
// hcode is a huffman code with a bit code and bit length.
|
||||||
|
type hcode uint32
|
||||||
|
|
||||||
|
func (h hcode) len() uint8 {
|
||||||
|
return uint8(h)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h hcode) code64() uint64 {
|
||||||
|
return uint64(h >> 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h hcode) zero() bool {
|
||||||
|
return h == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type huffmanEncoder struct {
|
||||||
|
codes []hcode
|
||||||
|
bitCount [17]int32
|
||||||
|
|
||||||
|
// Allocate a reusable buffer with the longest possible frequency table.
|
||||||
|
// Possible lengths are codegenCodeCount, offsetCodeCount and literalCount.
|
||||||
|
// The largest of these is literalCount, so we allocate for that case.
|
||||||
|
freqcache [literalCount + 1]literalNode
|
||||||
|
}
|
||||||
|
|
||||||
|
type literalNode struct {
|
||||||
|
literal uint16
|
||||||
|
freq uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// A levelInfo describes the state of the constructed tree for a given depth.
|
||||||
|
type levelInfo struct {
|
||||||
|
// Our level. for better printing
|
||||||
|
level int32
|
||||||
|
|
||||||
|
// The frequency of the last node at this level
|
||||||
|
lastFreq int32
|
||||||
|
|
||||||
|
// The frequency of the next character to add to this level
|
||||||
|
nextCharFreq int32
|
||||||
|
|
||||||
|
// The frequency of the next pair (from level below) to add to this level.
|
||||||
|
// Only valid if the "needed" value of the next lower level is 0.
|
||||||
|
nextPairFreq int32
|
||||||
|
|
||||||
|
// The number of chains remaining to generate for this level before moving
|
||||||
|
// up to the next level
|
||||||
|
needed int32
|
||||||
|
}
|
||||||
|
|
||||||
|
// set sets the code and length of an hcode.
|
||||||
|
func (h *hcode) set(code uint16, length uint8) {
|
||||||
|
*h = hcode(length) | (hcode(code) << 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newhcode(code uint16, length uint8) hcode {
|
||||||
|
return hcode(length) | (hcode(code) << 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
func reverseBits(number uint16, bitLength byte) uint16 {
|
||||||
|
return bits.Reverse16(number << ((16 - bitLength) & 15))
|
||||||
|
}
|
||||||
|
|
||||||
|
func maxNode() literalNode { return literalNode{math.MaxUint16, math.MaxUint16} }
|
||||||
|
|
||||||
|
func newHuffmanEncoder(size int) *huffmanEncoder {
|
||||||
|
// Make capacity to next power of two.
|
||||||
|
c := uint(bits.Len32(uint32(size - 1)))
|
||||||
|
return &huffmanEncoder{codes: make([]hcode, size, 1<<c)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generates a HuffmanCode corresponding to the fixed literal table
|
||||||
|
func generateFixedLiteralEncoding() *huffmanEncoder {
|
||||||
|
h := newHuffmanEncoder(literalCount)
|
||||||
|
codes := h.codes
|
||||||
|
var ch uint16
|
||||||
|
for ch = 0; ch < literalCount; ch++ {
|
||||||
|
var bits uint16
|
||||||
|
var size uint8
|
||||||
|
switch {
|
||||||
|
case ch < 144:
|
||||||
|
// size 8, 000110000 .. 10111111
|
||||||
|
bits = ch + 48
|
||||||
|
size = 8
|
||||||
|
case ch < 256:
|
||||||
|
// size 9, 110010000 .. 111111111
|
||||||
|
bits = ch + 400 - 144
|
||||||
|
size = 9
|
||||||
|
case ch < 280:
|
||||||
|
// size 7, 0000000 .. 0010111
|
||||||
|
bits = ch - 256
|
||||||
|
size = 7
|
||||||
|
default:
|
||||||
|
// size 8, 11000000 .. 11000111
|
||||||
|
bits = ch + 192 - 280
|
||||||
|
size = 8
|
||||||
|
}
|
||||||
|
codes[ch] = newhcode(reverseBits(bits, size), size)
|
||||||
|
}
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateFixedOffsetEncoding() *huffmanEncoder {
|
||||||
|
h := newHuffmanEncoder(30)
|
||||||
|
codes := h.codes
|
||||||
|
for ch := range codes {
|
||||||
|
codes[ch] = newhcode(reverseBits(uint16(ch), 5), 5)
|
||||||
|
}
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
var fixedLiteralEncoding = generateFixedLiteralEncoding()
|
||||||
|
var fixedOffsetEncoding = generateFixedOffsetEncoding()
|
||||||
|
|
||||||
|
func (h *huffmanEncoder) bitLength(freq []uint16) int {
|
||||||
|
var total int
|
||||||
|
for i, f := range freq {
|
||||||
|
if f != 0 {
|
||||||
|
total += int(f) * int(h.codes[i].len())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return total
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *huffmanEncoder) bitLengthRaw(b []byte) int {
|
||||||
|
var total int
|
||||||
|
for _, f := range b {
|
||||||
|
total += int(h.codes[f].len())
|
||||||
|
}
|
||||||
|
return total
|
||||||
|
}
|
||||||
|
|
||||||
|
// canReuseBits returns the number of bits or math.MaxInt32 if the encoder cannot be reused.
|
||||||
|
func (h *huffmanEncoder) canReuseBits(freq []uint16) int {
|
||||||
|
var total int
|
||||||
|
for i, f := range freq {
|
||||||
|
if f != 0 {
|
||||||
|
code := h.codes[i]
|
||||||
|
if code.zero() {
|
||||||
|
return math.MaxInt32
|
||||||
|
}
|
||||||
|
total += int(f) * int(code.len())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return total
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the number of literals assigned to each bit size in the Huffman encoding
|
||||||
|
//
|
||||||
|
// This method is only called when list.length >= 3
|
||||||
|
// The cases of 0, 1, and 2 literals are handled by special case code.
|
||||||
|
//
|
||||||
|
// list An array of the literals with non-zero frequencies
|
||||||
|
//
|
||||||
|
// and their associated frequencies. The array is in order of increasing
|
||||||
|
// frequency, and has as its last element a special element with frequency
|
||||||
|
// MaxInt32
|
||||||
|
//
|
||||||
|
// maxBits The maximum number of bits that should be used to encode any literal.
|
||||||
|
//
|
||||||
|
// Must be less than 16.
|
||||||
|
//
|
||||||
|
// return An integer array in which array[i] indicates the number of literals
|
||||||
|
//
|
||||||
|
// that should be encoded in i bits.
|
||||||
|
func (h *huffmanEncoder) bitCounts(list []literalNode, maxBits int32) []int32 {
|
||||||
|
if maxBits >= maxBitsLimit {
|
||||||
|
panic("flate: maxBits too large")
|
||||||
|
}
|
||||||
|
n := int32(len(list))
|
||||||
|
list = list[0 : n+1]
|
||||||
|
list[n] = maxNode()
|
||||||
|
|
||||||
|
// The tree can't have greater depth than n - 1, no matter what. This
|
||||||
|
// saves a little bit of work in some small cases
|
||||||
|
if maxBits > n-1 {
|
||||||
|
maxBits = n - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create information about each of the levels.
|
||||||
|
// A bogus "Level 0" whose sole purpose is so that
|
||||||
|
// level1.prev.needed==0. This makes level1.nextPairFreq
|
||||||
|
// be a legitimate value that never gets chosen.
|
||||||
|
var levels [maxBitsLimit]levelInfo
|
||||||
|
// leafCounts[i] counts the number of literals at the left
|
||||||
|
// of ancestors of the rightmost node at level i.
|
||||||
|
// leafCounts[i][j] is the number of literals at the left
|
||||||
|
// of the level j ancestor.
|
||||||
|
var leafCounts [maxBitsLimit][maxBitsLimit]int32
|
||||||
|
|
||||||
|
// Descending to only have 1 bounds check.
|
||||||
|
l2f := int32(list[2].freq)
|
||||||
|
l1f := int32(list[1].freq)
|
||||||
|
l0f := int32(list[0].freq) + int32(list[1].freq)
|
||||||
|
|
||||||
|
for level := int32(1); level <= maxBits; level++ {
|
||||||
|
// For every level, the first two items are the first two characters.
|
||||||
|
// We initialize the levels as if we had already figured this out.
|
||||||
|
levels[level] = levelInfo{
|
||||||
|
level: level,
|
||||||
|
lastFreq: l1f,
|
||||||
|
nextCharFreq: l2f,
|
||||||
|
nextPairFreq: l0f,
|
||||||
|
}
|
||||||
|
leafCounts[level][level] = 2
|
||||||
|
if level == 1 {
|
||||||
|
levels[level].nextPairFreq = math.MaxInt32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need a total of 2*n - 2 items at top level and have already generated 2.
|
||||||
|
levels[maxBits].needed = 2*n - 4
|
||||||
|
|
||||||
|
level := uint32(maxBits)
|
||||||
|
for level < 16 {
|
||||||
|
l := &levels[level]
|
||||||
|
if l.nextPairFreq == math.MaxInt32 && l.nextCharFreq == math.MaxInt32 {
|
||||||
|
// We've run out of both leafs and pairs.
|
||||||
|
// End all calculations for this level.
|
||||||
|
// To make sure we never come back to this level or any lower level,
|
||||||
|
// set nextPairFreq impossibly large.
|
||||||
|
l.needed = 0
|
||||||
|
levels[level+1].nextPairFreq = math.MaxInt32
|
||||||
|
level++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
prevFreq := l.lastFreq
|
||||||
|
if l.nextCharFreq < l.nextPairFreq {
|
||||||
|
// The next item on this row is a leaf node.
|
||||||
|
n := leafCounts[level][level] + 1
|
||||||
|
l.lastFreq = l.nextCharFreq
|
||||||
|
// Lower leafCounts are the same of the previous node.
|
||||||
|
leafCounts[level][level] = n
|
||||||
|
e := list[n]
|
||||||
|
if e.literal < math.MaxUint16 {
|
||||||
|
l.nextCharFreq = int32(e.freq)
|
||||||
|
} else {
|
||||||
|
l.nextCharFreq = math.MaxInt32
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// The next item on this row is a pair from the previous row.
|
||||||
|
// nextPairFreq isn't valid until we generate two
|
||||||
|
// more values in the level below
|
||||||
|
l.lastFreq = l.nextPairFreq
|
||||||
|
// Take leaf counts from the lower level, except counts[level] remains the same.
|
||||||
|
if true {
|
||||||
|
save := leafCounts[level][level]
|
||||||
|
leafCounts[level] = leafCounts[level-1]
|
||||||
|
leafCounts[level][level] = save
|
||||||
|
} else {
|
||||||
|
copy(leafCounts[level][:level], leafCounts[level-1][:level])
|
||||||
|
}
|
||||||
|
levels[l.level-1].needed = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
if l.needed--; l.needed == 0 {
|
||||||
|
// We've done everything we need to do for this level.
|
||||||
|
// Continue calculating one level up. Fill in nextPairFreq
|
||||||
|
// of that level with the sum of the two nodes we've just calculated on
|
||||||
|
// this level.
|
||||||
|
if l.level == maxBits {
|
||||||
|
// All done!
|
||||||
|
break
|
||||||
|
}
|
||||||
|
levels[l.level+1].nextPairFreq = prevFreq + l.lastFreq
|
||||||
|
level++
|
||||||
|
} else {
|
||||||
|
// If we stole from below, move down temporarily to replenish it.
|
||||||
|
for levels[level-1].needed > 0 {
|
||||||
|
level--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Somethings is wrong if at the end, the top level is null or hasn't used
|
||||||
|
// all of the leaves.
|
||||||
|
if leafCounts[maxBits][maxBits] != n {
|
||||||
|
panic("leafCounts[maxBits][maxBits] != n")
|
||||||
|
}
|
||||||
|
|
||||||
|
bitCount := h.bitCount[:maxBits+1]
|
||||||
|
bits := 1
|
||||||
|
counts := &leafCounts[maxBits]
|
||||||
|
for level := maxBits; level > 0; level-- {
|
||||||
|
// chain.leafCount gives the number of literals requiring at least "bits"
|
||||||
|
// bits to encode.
|
||||||
|
bitCount[bits] = counts[level] - counts[level-1]
|
||||||
|
bits++
|
||||||
|
}
|
||||||
|
return bitCount
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look at the leaves and assign them a bit count and an encoding as specified
|
||||||
|
// in RFC 1951 3.2.2
|
||||||
|
func (h *huffmanEncoder) assignEncodingAndSize(bitCount []int32, list []literalNode) {
|
||||||
|
code := uint16(0)
|
||||||
|
for n, bits := range bitCount {
|
||||||
|
code <<= 1
|
||||||
|
if n == 0 || bits == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// The literals list[len(list)-bits] .. list[len(list)-bits]
|
||||||
|
// are encoded using "bits" bits, and get the values
|
||||||
|
// code, code + 1, .... The code values are
|
||||||
|
// assigned in literal order (not frequency order).
|
||||||
|
chunk := list[len(list)-int(bits):]
|
||||||
|
|
||||||
|
sortByLiteral(chunk)
|
||||||
|
for _, node := range chunk {
|
||||||
|
h.codes[node.literal] = newhcode(reverseBits(code, uint8(n)), uint8(n))
|
||||||
|
code++
|
||||||
|
}
|
||||||
|
list = list[0 : len(list)-int(bits)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update this Huffman Code object to be the minimum code for the specified frequency count.
|
||||||
|
//
|
||||||
|
// freq An array of frequencies, in which frequency[i] gives the frequency of literal i.
|
||||||
|
// maxBits The maximum number of bits to use for any literal.
|
||||||
|
func (h *huffmanEncoder) generate(freq []uint16, maxBits int32) {
|
||||||
|
list := h.freqcache[:len(freq)+1]
|
||||||
|
codes := h.codes[:len(freq)]
|
||||||
|
// Number of non-zero literals
|
||||||
|
count := 0
|
||||||
|
// Set list to be the set of all non-zero literals and their frequencies
|
||||||
|
for i, f := range freq {
|
||||||
|
if f != 0 {
|
||||||
|
list[count] = literalNode{uint16(i), f}
|
||||||
|
count++
|
||||||
|
} else {
|
||||||
|
codes[i] = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
list[count] = literalNode{}
|
||||||
|
|
||||||
|
list = list[:count]
|
||||||
|
if count <= 2 {
|
||||||
|
// Handle the small cases here, because they are awkward for the general case code. With
|
||||||
|
// two or fewer literals, everything has bit length 1.
|
||||||
|
for i, node := range list {
|
||||||
|
// "list" is in order of increasing literal value.
|
||||||
|
h.codes[node.literal].set(uint16(i), 1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sortByFreq(list)
|
||||||
|
|
||||||
|
// Get the number of literals for each bit count
|
||||||
|
bitCount := h.bitCounts(list, maxBits)
|
||||||
|
// And do the assignment
|
||||||
|
h.assignEncodingAndSize(bitCount, list)
|
||||||
|
}
|
||||||
|
|
||||||
|
// atLeastOne clamps the result between 1 and 15.
|
||||||
|
func atLeastOne(v float32) float32 {
|
||||||
|
if v < 1 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if v > 15 {
|
||||||
|
return 15
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func histogram(b []byte, h []uint16) {
|
||||||
|
if true && len(b) >= 8<<10 {
|
||||||
|
// Split for bigger inputs
|
||||||
|
histogramSplit(b, h)
|
||||||
|
} else {
|
||||||
|
h = h[:256]
|
||||||
|
for _, t := range b {
|
||||||
|
h[t]++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func histogramSplit(b []byte, h []uint16) {
|
||||||
|
// Tested, and slightly faster than 2-way.
|
||||||
|
// Writing to separate arrays and combining is also slightly slower.
|
||||||
|
h = h[:256]
|
||||||
|
for len(b)&3 != 0 {
|
||||||
|
h[b[0]]++
|
||||||
|
b = b[1:]
|
||||||
|
}
|
||||||
|
n := len(b) / 4
|
||||||
|
x, y, z, w := b[:n], b[n:], b[n+n:], b[n+n+n:]
|
||||||
|
y, z, w = y[:len(x)], z[:len(x)], w[:len(x)]
|
||||||
|
for i, t := range x {
|
||||||
|
v0 := &h[t]
|
||||||
|
v1 := &h[y[i]]
|
||||||
|
v3 := &h[w[i]]
|
||||||
|
v2 := &h[z[i]]
|
||||||
|
*v0++
|
||||||
|
*v1++
|
||||||
|
*v2++
|
||||||
|
*v3++
|
||||||
|
}
|
||||||
|
}
|
||||||
178
vendor/github.com/klauspost/compress/flate/huffman_sortByFreq.go
generated
vendored
Normal file
178
vendor/github.com/klauspost/compress/flate/huffman_sortByFreq.go
generated
vendored
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package flate
|
||||||
|
|
||||||
|
// Sort sorts data.
|
||||||
|
// It makes one call to data.Len to determine n, and O(n*log(n)) calls to
|
||||||
|
// data.Less and data.Swap. The sort is not guaranteed to be stable.
|
||||||
|
func sortByFreq(data []literalNode) {
|
||||||
|
n := len(data)
|
||||||
|
quickSortByFreq(data, 0, n, maxDepth(n))
|
||||||
|
}
|
||||||
|
|
||||||
|
func quickSortByFreq(data []literalNode, a, b, maxDepth int) {
|
||||||
|
for b-a > 12 { // Use ShellSort for slices <= 12 elements
|
||||||
|
if maxDepth == 0 {
|
||||||
|
heapSort(data, a, b)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
maxDepth--
|
||||||
|
mlo, mhi := doPivotByFreq(data, a, b)
|
||||||
|
// Avoiding recursion on the larger subproblem guarantees
|
||||||
|
// a stack depth of at most lg(b-a).
|
||||||
|
if mlo-a < b-mhi {
|
||||||
|
quickSortByFreq(data, a, mlo, maxDepth)
|
||||||
|
a = mhi // i.e., quickSortByFreq(data, mhi, b)
|
||||||
|
} else {
|
||||||
|
quickSortByFreq(data, mhi, b, maxDepth)
|
||||||
|
b = mlo // i.e., quickSortByFreq(data, a, mlo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if b-a > 1 {
|
||||||
|
// Do ShellSort pass with gap 6
|
||||||
|
// It could be written in this simplified form cause b-a <= 12
|
||||||
|
for i := a + 6; i < b; i++ {
|
||||||
|
if data[i].freq == data[i-6].freq && data[i].literal < data[i-6].literal || data[i].freq < data[i-6].freq {
|
||||||
|
data[i], data[i-6] = data[i-6], data[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
insertionSortByFreq(data, a, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// siftDownByFreq implements the heap property on data[lo, hi).
|
||||||
|
// first is an offset into the array where the root of the heap lies.
|
||||||
|
func siftDownByFreq(data []literalNode, lo, hi, first int) {
|
||||||
|
root := lo
|
||||||
|
for {
|
||||||
|
child := 2*root + 1
|
||||||
|
if child >= hi {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if child+1 < hi && (data[first+child].freq == data[first+child+1].freq && data[first+child].literal < data[first+child+1].literal || data[first+child].freq < data[first+child+1].freq) {
|
||||||
|
child++
|
||||||
|
}
|
||||||
|
if data[first+root].freq == data[first+child].freq && data[first+root].literal > data[first+child].literal || data[first+root].freq > data[first+child].freq {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data[first+root], data[first+child] = data[first+child], data[first+root]
|
||||||
|
root = child
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func doPivotByFreq(data []literalNode, lo, hi int) (midlo, midhi int) {
|
||||||
|
m := int(uint(lo+hi) >> 1) // Written like this to avoid integer overflow.
|
||||||
|
if hi-lo > 40 {
|
||||||
|
// Tukey's ``Ninther,'' median of three medians of three.
|
||||||
|
s := (hi - lo) / 8
|
||||||
|
medianOfThreeSortByFreq(data, lo, lo+s, lo+2*s)
|
||||||
|
medianOfThreeSortByFreq(data, m, m-s, m+s)
|
||||||
|
medianOfThreeSortByFreq(data, hi-1, hi-1-s, hi-1-2*s)
|
||||||
|
}
|
||||||
|
medianOfThreeSortByFreq(data, lo, m, hi-1)
|
||||||
|
|
||||||
|
// Invariants are:
|
||||||
|
// data[lo] = pivot (set up by ChoosePivot)
|
||||||
|
// data[lo < i < a] < pivot
|
||||||
|
// data[a <= i < b] <= pivot
|
||||||
|
// data[b <= i < c] unexamined
|
||||||
|
// data[c <= i < hi-1] > pivot
|
||||||
|
// data[hi-1] >= pivot
|
||||||
|
pivot := lo
|
||||||
|
a, c := lo+1, hi-1
|
||||||
|
|
||||||
|
for ; a < c && (data[a].freq == data[pivot].freq && data[a].literal < data[pivot].literal || data[a].freq < data[pivot].freq); a++ {
|
||||||
|
}
|
||||||
|
b := a
|
||||||
|
for {
|
||||||
|
for ; b < c && (data[pivot].freq == data[b].freq && data[pivot].literal > data[b].literal || data[pivot].freq > data[b].freq); b++ { // data[b] <= pivot
|
||||||
|
}
|
||||||
|
for ; b < c && (data[pivot].freq == data[c-1].freq && data[pivot].literal < data[c-1].literal || data[pivot].freq < data[c-1].freq); c-- { // data[c-1] > pivot
|
||||||
|
}
|
||||||
|
if b >= c {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// data[b] > pivot; data[c-1] <= pivot
|
||||||
|
data[b], data[c-1] = data[c-1], data[b]
|
||||||
|
b++
|
||||||
|
c--
|
||||||
|
}
|
||||||
|
// If hi-c<3 then there are duplicates (by property of median of nine).
|
||||||
|
// Let's be a bit more conservative, and set border to 5.
|
||||||
|
protect := hi-c < 5
|
||||||
|
if !protect && hi-c < (hi-lo)/4 {
|
||||||
|
// Lets test some points for equality to pivot
|
||||||
|
dups := 0
|
||||||
|
if data[pivot].freq == data[hi-1].freq && data[pivot].literal > data[hi-1].literal || data[pivot].freq > data[hi-1].freq { // data[hi-1] = pivot
|
||||||
|
data[c], data[hi-1] = data[hi-1], data[c]
|
||||||
|
c++
|
||||||
|
dups++
|
||||||
|
}
|
||||||
|
if data[b-1].freq == data[pivot].freq && data[b-1].literal > data[pivot].literal || data[b-1].freq > data[pivot].freq { // data[b-1] = pivot
|
||||||
|
b--
|
||||||
|
dups++
|
||||||
|
}
|
||||||
|
// m-lo = (hi-lo)/2 > 6
|
||||||
|
// b-lo > (hi-lo)*3/4-1 > 8
|
||||||
|
// ==> m < b ==> data[m] <= pivot
|
||||||
|
if data[m].freq == data[pivot].freq && data[m].literal > data[pivot].literal || data[m].freq > data[pivot].freq { // data[m] = pivot
|
||||||
|
data[m], data[b-1] = data[b-1], data[m]
|
||||||
|
b--
|
||||||
|
dups++
|
||||||
|
}
|
||||||
|
// if at least 2 points are equal to pivot, assume skewed distribution
|
||||||
|
protect = dups > 1
|
||||||
|
}
|
||||||
|
if protect {
|
||||||
|
// Protect against a lot of duplicates
|
||||||
|
// Add invariant:
|
||||||
|
// data[a <= i < b] unexamined
|
||||||
|
// data[b <= i < c] = pivot
|
||||||
|
for {
|
||||||
|
for ; a < b && (data[b-1].freq == data[pivot].freq && data[b-1].literal > data[pivot].literal || data[b-1].freq > data[pivot].freq); b-- { // data[b] == pivot
|
||||||
|
}
|
||||||
|
for ; a < b && (data[a].freq == data[pivot].freq && data[a].literal < data[pivot].literal || data[a].freq < data[pivot].freq); a++ { // data[a] < pivot
|
||||||
|
}
|
||||||
|
if a >= b {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// data[a] == pivot; data[b-1] < pivot
|
||||||
|
data[a], data[b-1] = data[b-1], data[a]
|
||||||
|
a++
|
||||||
|
b--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Swap pivot into middle
|
||||||
|
data[pivot], data[b-1] = data[b-1], data[pivot]
|
||||||
|
return b - 1, c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insertion sort
|
||||||
|
func insertionSortByFreq(data []literalNode, a, b int) {
|
||||||
|
for i := a + 1; i < b; i++ {
|
||||||
|
for j := i; j > a && (data[j].freq == data[j-1].freq && data[j].literal < data[j-1].literal || data[j].freq < data[j-1].freq); j-- {
|
||||||
|
data[j], data[j-1] = data[j-1], data[j]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// quickSortByFreq, loosely following Bentley and McIlroy,
|
||||||
|
// ``Engineering a Sort Function,'' SP&E November 1993.
|
||||||
|
|
||||||
|
// medianOfThreeSortByFreq moves the median of the three values data[m0], data[m1], data[m2] into data[m1].
|
||||||
|
func medianOfThreeSortByFreq(data []literalNode, m1, m0, m2 int) {
|
||||||
|
// sort 3 elements
|
||||||
|
if data[m1].freq == data[m0].freq && data[m1].literal < data[m0].literal || data[m1].freq < data[m0].freq {
|
||||||
|
data[m1], data[m0] = data[m0], data[m1]
|
||||||
|
}
|
||||||
|
// data[m0] <= data[m1]
|
||||||
|
if data[m2].freq == data[m1].freq && data[m2].literal < data[m1].literal || data[m2].freq < data[m1].freq {
|
||||||
|
data[m2], data[m1] = data[m1], data[m2]
|
||||||
|
// data[m0] <= data[m2] && data[m1] < data[m2]
|
||||||
|
if data[m1].freq == data[m0].freq && data[m1].literal < data[m0].literal || data[m1].freq < data[m0].freq {
|
||||||
|
data[m1], data[m0] = data[m0], data[m1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// now data[m0] <= data[m1] <= data[m2]
|
||||||
|
}
|
||||||
201
vendor/github.com/klauspost/compress/flate/huffman_sortByLiteral.go
generated
vendored
Normal file
201
vendor/github.com/klauspost/compress/flate/huffman_sortByLiteral.go
generated
vendored
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package flate
|
||||||
|
|
||||||
|
// Sort sorts data.
|
||||||
|
// It makes one call to data.Len to determine n, and O(n*log(n)) calls to
|
||||||
|
// data.Less and data.Swap. The sort is not guaranteed to be stable.
|
||||||
|
func sortByLiteral(data []literalNode) {
|
||||||
|
n := len(data)
|
||||||
|
quickSort(data, 0, n, maxDepth(n))
|
||||||
|
}
|
||||||
|
|
||||||
|
func quickSort(data []literalNode, a, b, maxDepth int) {
|
||||||
|
for b-a > 12 { // Use ShellSort for slices <= 12 elements
|
||||||
|
if maxDepth == 0 {
|
||||||
|
heapSort(data, a, b)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
maxDepth--
|
||||||
|
mlo, mhi := doPivot(data, a, b)
|
||||||
|
// Avoiding recursion on the larger subproblem guarantees
|
||||||
|
// a stack depth of at most lg(b-a).
|
||||||
|
if mlo-a < b-mhi {
|
||||||
|
quickSort(data, a, mlo, maxDepth)
|
||||||
|
a = mhi // i.e., quickSort(data, mhi, b)
|
||||||
|
} else {
|
||||||
|
quickSort(data, mhi, b, maxDepth)
|
||||||
|
b = mlo // i.e., quickSort(data, a, mlo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if b-a > 1 {
|
||||||
|
// Do ShellSort pass with gap 6
|
||||||
|
// It could be written in this simplified form cause b-a <= 12
|
||||||
|
for i := a + 6; i < b; i++ {
|
||||||
|
if data[i].literal < data[i-6].literal {
|
||||||
|
data[i], data[i-6] = data[i-6], data[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
insertionSort(data, a, b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func heapSort(data []literalNode, a, b int) {
|
||||||
|
first := a
|
||||||
|
lo := 0
|
||||||
|
hi := b - a
|
||||||
|
|
||||||
|
// Build heap with greatest element at top.
|
||||||
|
for i := (hi - 1) / 2; i >= 0; i-- {
|
||||||
|
siftDown(data, i, hi, first)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pop elements, largest first, into end of data.
|
||||||
|
for i := hi - 1; i >= 0; i-- {
|
||||||
|
data[first], data[first+i] = data[first+i], data[first]
|
||||||
|
siftDown(data, lo, i, first)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// siftDown implements the heap property on data[lo, hi).
|
||||||
|
// first is an offset into the array where the root of the heap lies.
|
||||||
|
func siftDown(data []literalNode, lo, hi, first int) {
|
||||||
|
root := lo
|
||||||
|
for {
|
||||||
|
child := 2*root + 1
|
||||||
|
if child >= hi {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if child+1 < hi && data[first+child].literal < data[first+child+1].literal {
|
||||||
|
child++
|
||||||
|
}
|
||||||
|
if data[first+root].literal > data[first+child].literal {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data[first+root], data[first+child] = data[first+child], data[first+root]
|
||||||
|
root = child
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func doPivot(data []literalNode, lo, hi int) (midlo, midhi int) {
|
||||||
|
m := int(uint(lo+hi) >> 1) // Written like this to avoid integer overflow.
|
||||||
|
if hi-lo > 40 {
|
||||||
|
// Tukey's ``Ninther,'' median of three medians of three.
|
||||||
|
s := (hi - lo) / 8
|
||||||
|
medianOfThree(data, lo, lo+s, lo+2*s)
|
||||||
|
medianOfThree(data, m, m-s, m+s)
|
||||||
|
medianOfThree(data, hi-1, hi-1-s, hi-1-2*s)
|
||||||
|
}
|
||||||
|
medianOfThree(data, lo, m, hi-1)
|
||||||
|
|
||||||
|
// Invariants are:
|
||||||
|
// data[lo] = pivot (set up by ChoosePivot)
|
||||||
|
// data[lo < i < a] < pivot
|
||||||
|
// data[a <= i < b] <= pivot
|
||||||
|
// data[b <= i < c] unexamined
|
||||||
|
// data[c <= i < hi-1] > pivot
|
||||||
|
// data[hi-1] >= pivot
|
||||||
|
pivot := lo
|
||||||
|
a, c := lo+1, hi-1
|
||||||
|
|
||||||
|
for ; a < c && data[a].literal < data[pivot].literal; a++ {
|
||||||
|
}
|
||||||
|
b := a
|
||||||
|
for {
|
||||||
|
for ; b < c && data[pivot].literal > data[b].literal; b++ { // data[b] <= pivot
|
||||||
|
}
|
||||||
|
for ; b < c && data[pivot].literal < data[c-1].literal; c-- { // data[c-1] > pivot
|
||||||
|
}
|
||||||
|
if b >= c {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// data[b] > pivot; data[c-1] <= pivot
|
||||||
|
data[b], data[c-1] = data[c-1], data[b]
|
||||||
|
b++
|
||||||
|
c--
|
||||||
|
}
|
||||||
|
// If hi-c<3 then there are duplicates (by property of median of nine).
|
||||||
|
// Let's be a bit more conservative, and set border to 5.
|
||||||
|
protect := hi-c < 5
|
||||||
|
if !protect && hi-c < (hi-lo)/4 {
|
||||||
|
// Lets test some points for equality to pivot
|
||||||
|
dups := 0
|
||||||
|
if data[pivot].literal > data[hi-1].literal { // data[hi-1] = pivot
|
||||||
|
data[c], data[hi-1] = data[hi-1], data[c]
|
||||||
|
c++
|
||||||
|
dups++
|
||||||
|
}
|
||||||
|
if data[b-1].literal > data[pivot].literal { // data[b-1] = pivot
|
||||||
|
b--
|
||||||
|
dups++
|
||||||
|
}
|
||||||
|
// m-lo = (hi-lo)/2 > 6
|
||||||
|
// b-lo > (hi-lo)*3/4-1 > 8
|
||||||
|
// ==> m < b ==> data[m] <= pivot
|
||||||
|
if data[m].literal > data[pivot].literal { // data[m] = pivot
|
||||||
|
data[m], data[b-1] = data[b-1], data[m]
|
||||||
|
b--
|
||||||
|
dups++
|
||||||
|
}
|
||||||
|
// if at least 2 points are equal to pivot, assume skewed distribution
|
||||||
|
protect = dups > 1
|
||||||
|
}
|
||||||
|
if protect {
|
||||||
|
// Protect against a lot of duplicates
|
||||||
|
// Add invariant:
|
||||||
|
// data[a <= i < b] unexamined
|
||||||
|
// data[b <= i < c] = pivot
|
||||||
|
for {
|
||||||
|
for ; a < b && data[b-1].literal > data[pivot].literal; b-- { // data[b] == pivot
|
||||||
|
}
|
||||||
|
for ; a < b && data[a].literal < data[pivot].literal; a++ { // data[a] < pivot
|
||||||
|
}
|
||||||
|
if a >= b {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// data[a] == pivot; data[b-1] < pivot
|
||||||
|
data[a], data[b-1] = data[b-1], data[a]
|
||||||
|
a++
|
||||||
|
b--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Swap pivot into middle
|
||||||
|
data[pivot], data[b-1] = data[b-1], data[pivot]
|
||||||
|
return b - 1, c
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insertion sort
|
||||||
|
func insertionSort(data []literalNode, a, b int) {
|
||||||
|
for i := a + 1; i < b; i++ {
|
||||||
|
for j := i; j > a && data[j].literal < data[j-1].literal; j-- {
|
||||||
|
data[j], data[j-1] = data[j-1], data[j]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// maxDepth returns a threshold at which quicksort should switch
|
||||||
|
// to heapsort. It returns 2*ceil(lg(n+1)).
|
||||||
|
func maxDepth(n int) int {
|
||||||
|
var depth int
|
||||||
|
for i := n; i > 0; i >>= 1 {
|
||||||
|
depth++
|
||||||
|
}
|
||||||
|
return depth * 2
|
||||||
|
}
|
||||||
|
|
||||||
|
// medianOfThree moves the median of the three values data[m0], data[m1], data[m2] into data[m1].
|
||||||
|
func medianOfThree(data []literalNode, m1, m0, m2 int) {
|
||||||
|
// sort 3 elements
|
||||||
|
if data[m1].literal < data[m0].literal {
|
||||||
|
data[m1], data[m0] = data[m0], data[m1]
|
||||||
|
}
|
||||||
|
// data[m0] <= data[m1]
|
||||||
|
if data[m2].literal < data[m1].literal {
|
||||||
|
data[m2], data[m1] = data[m1], data[m2]
|
||||||
|
// data[m0] <= data[m2] && data[m1] < data[m2]
|
||||||
|
if data[m1].literal < data[m0].literal {
|
||||||
|
data[m1], data[m0] = data[m0], data[m1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// now data[m0] <= data[m1] <= data[m2]
|
||||||
|
}
|
||||||
793
vendor/github.com/klauspost/compress/flate/inflate.go
generated
vendored
Normal file
793
vendor/github.com/klauspost/compress/flate/inflate.go
generated
vendored
Normal file
@@ -0,0 +1,793 @@
|
|||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// Package flate implements the DEFLATE compressed data format, described in
|
||||||
|
// RFC 1951. The gzip and zlib packages implement access to DEFLATE-based file
|
||||||
|
// formats.
|
||||||
|
package flate
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"compress/flate"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math/bits"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
maxCodeLen = 16 // max length of Huffman code
|
||||||
|
maxCodeLenMask = 15 // mask for max length of Huffman code
|
||||||
|
// The next three numbers come from the RFC section 3.2.7, with the
|
||||||
|
// additional proviso in section 3.2.5 which implies that distance codes
|
||||||
|
// 30 and 31 should never occur in compressed data.
|
||||||
|
maxNumLit = 286
|
||||||
|
maxNumDist = 30
|
||||||
|
numCodes = 19 // number of codes in Huffman meta-code
|
||||||
|
|
||||||
|
debugDecode = false
|
||||||
|
)
|
||||||
|
|
||||||
|
// Value of length - 3 and extra bits.
|
||||||
|
type lengthExtra struct {
|
||||||
|
length, extra uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
var decCodeToLen = [32]lengthExtra{{length: 0x0, extra: 0x0}, {length: 0x1, extra: 0x0}, {length: 0x2, extra: 0x0}, {length: 0x3, extra: 0x0}, {length: 0x4, extra: 0x0}, {length: 0x5, extra: 0x0}, {length: 0x6, extra: 0x0}, {length: 0x7, extra: 0x0}, {length: 0x8, extra: 0x1}, {length: 0xa, extra: 0x1}, {length: 0xc, extra: 0x1}, {length: 0xe, extra: 0x1}, {length: 0x10, extra: 0x2}, {length: 0x14, extra: 0x2}, {length: 0x18, extra: 0x2}, {length: 0x1c, extra: 0x2}, {length: 0x20, extra: 0x3}, {length: 0x28, extra: 0x3}, {length: 0x30, extra: 0x3}, {length: 0x38, extra: 0x3}, {length: 0x40, extra: 0x4}, {length: 0x50, extra: 0x4}, {length: 0x60, extra: 0x4}, {length: 0x70, extra: 0x4}, {length: 0x80, extra: 0x5}, {length: 0xa0, extra: 0x5}, {length: 0xc0, extra: 0x5}, {length: 0xe0, extra: 0x5}, {length: 0xff, extra: 0x0}, {length: 0x0, extra: 0x0}, {length: 0x0, extra: 0x0}, {length: 0x0, extra: 0x0}}
|
||||||
|
|
||||||
|
var bitMask32 = [32]uint32{
|
||||||
|
0, 1, 3, 7, 0xF, 0x1F, 0x3F, 0x7F, 0xFF,
|
||||||
|
0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF,
|
||||||
|
0x1ffff, 0x3ffff, 0x7FFFF, 0xfFFFF, 0x1fFFFF, 0x3fFFFF, 0x7fFFFF, 0xffFFFF,
|
||||||
|
0x1ffFFFF, 0x3ffFFFF, 0x7ffFFFF, 0xfffFFFF, 0x1fffFFFF, 0x3fffFFFF, 0x7fffFFFF,
|
||||||
|
} // up to 32 bits
|
||||||
|
|
||||||
|
// Initialize the fixedHuffmanDecoder only once upon first use.
|
||||||
|
var fixedOnce sync.Once
|
||||||
|
var fixedHuffmanDecoder huffmanDecoder
|
||||||
|
|
||||||
|
// A CorruptInputError reports the presence of corrupt input at a given offset.
|
||||||
|
type CorruptInputError = flate.CorruptInputError
|
||||||
|
|
||||||
|
// An InternalError reports an error in the flate code itself.
|
||||||
|
type InternalError string
|
||||||
|
|
||||||
|
func (e InternalError) Error() string { return "flate: internal error: " + string(e) }
|
||||||
|
|
||||||
|
// A ReadError reports an error encountered while reading input.
|
||||||
|
//
|
||||||
|
// Deprecated: No longer returned.
|
||||||
|
type ReadError = flate.ReadError
|
||||||
|
|
||||||
|
// A WriteError reports an error encountered while writing output.
|
||||||
|
//
|
||||||
|
// Deprecated: No longer returned.
|
||||||
|
type WriteError = flate.WriteError
|
||||||
|
|
||||||
|
// Resetter resets a ReadCloser returned by NewReader or NewReaderDict to
|
||||||
|
// to switch to a new underlying Reader. This permits reusing a ReadCloser
|
||||||
|
// instead of allocating a new one.
|
||||||
|
type Resetter interface {
|
||||||
|
// Reset discards any buffered data and resets the Resetter as if it was
|
||||||
|
// newly initialized with the given reader.
|
||||||
|
Reset(r io.Reader, dict []byte) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// The data structure for decoding Huffman tables is based on that of
|
||||||
|
// zlib. There is a lookup table of a fixed bit width (huffmanChunkBits),
|
||||||
|
// For codes smaller than the table width, there are multiple entries
|
||||||
|
// (each combination of trailing bits has the same value). For codes
|
||||||
|
// larger than the table width, the table contains a link to an overflow
|
||||||
|
// table. The width of each entry in the link table is the maximum code
|
||||||
|
// size minus the chunk width.
|
||||||
|
//
|
||||||
|
// Note that you can do a lookup in the table even without all bits
|
||||||
|
// filled. Since the extra bits are zero, and the DEFLATE Huffman codes
|
||||||
|
// have the property that shorter codes come before longer ones, the
|
||||||
|
// bit length estimate in the result is a lower bound on the actual
|
||||||
|
// number of bits.
|
||||||
|
//
|
||||||
|
// See the following:
|
||||||
|
// http://www.gzip.org/algorithm.txt
|
||||||
|
|
||||||
|
// chunk & 15 is number of bits
|
||||||
|
// chunk >> 4 is value, including table link
|
||||||
|
|
||||||
|
const (
|
||||||
|
huffmanChunkBits = 9
|
||||||
|
huffmanNumChunks = 1 << huffmanChunkBits
|
||||||
|
huffmanCountMask = 15
|
||||||
|
huffmanValueShift = 4
|
||||||
|
)
|
||||||
|
|
||||||
|
type huffmanDecoder struct {
|
||||||
|
maxRead int // the maximum number of bits we can read and not overread
|
||||||
|
chunks *[huffmanNumChunks]uint16 // chunks as described above
|
||||||
|
links [][]uint16 // overflow links
|
||||||
|
linkMask uint32 // mask the width of the link table
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize Huffman decoding tables from array of code lengths.
|
||||||
|
// Following this function, h is guaranteed to be initialized into a complete
|
||||||
|
// tree (i.e., neither over-subscribed nor under-subscribed). The exception is a
|
||||||
|
// degenerate case where the tree has only a single symbol with length 1. Empty
|
||||||
|
// trees are permitted.
|
||||||
|
func (h *huffmanDecoder) init(lengths []int) bool {
|
||||||
|
// Sanity enables additional runtime tests during Huffman
|
||||||
|
// table construction. It's intended to be used during
|
||||||
|
// development to supplement the currently ad-hoc unit tests.
|
||||||
|
const sanity = false
|
||||||
|
|
||||||
|
if h.chunks == nil {
|
||||||
|
h.chunks = &[huffmanNumChunks]uint16{}
|
||||||
|
}
|
||||||
|
if h.maxRead != 0 {
|
||||||
|
*h = huffmanDecoder{chunks: h.chunks, links: h.links}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count number of codes of each length,
|
||||||
|
// compute maxRead and max length.
|
||||||
|
var count [maxCodeLen]int
|
||||||
|
var min, max int
|
||||||
|
for _, n := range lengths {
|
||||||
|
if n == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if min == 0 || n < min {
|
||||||
|
min = n
|
||||||
|
}
|
||||||
|
if n > max {
|
||||||
|
max = n
|
||||||
|
}
|
||||||
|
count[n&maxCodeLenMask]++
|
||||||
|
}
|
||||||
|
|
||||||
|
// Empty tree. The decompressor.huffSym function will fail later if the tree
|
||||||
|
// is used. Technically, an empty tree is only valid for the HDIST tree and
|
||||||
|
// not the HCLEN and HLIT tree. However, a stream with an empty HCLEN tree
|
||||||
|
// is guaranteed to fail since it will attempt to use the tree to decode the
|
||||||
|
// codes for the HLIT and HDIST trees. Similarly, an empty HLIT tree is
|
||||||
|
// guaranteed to fail later since the compressed data section must be
|
||||||
|
// composed of at least one symbol (the end-of-block marker).
|
||||||
|
if max == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
code := 0
|
||||||
|
var nextcode [maxCodeLen]int
|
||||||
|
for i := min; i <= max; i++ {
|
||||||
|
code <<= 1
|
||||||
|
nextcode[i&maxCodeLenMask] = code
|
||||||
|
code += count[i&maxCodeLenMask]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the coding is complete (i.e., that we've
|
||||||
|
// assigned all 2-to-the-max possible bit sequences).
|
||||||
|
// Exception: To be compatible with zlib, we also need to
|
||||||
|
// accept degenerate single-code codings. See also
|
||||||
|
// TestDegenerateHuffmanCoding.
|
||||||
|
if code != 1<<uint(max) && !(code == 1 && max == 1) {
|
||||||
|
if debugDecode {
|
||||||
|
fmt.Println("coding failed, code, max:", code, max, code == 1<<uint(max), code == 1 && max == 1, "(one should be true)")
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
h.maxRead = min
|
||||||
|
chunks := h.chunks[:]
|
||||||
|
for i := range chunks {
|
||||||
|
chunks[i] = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if max > huffmanChunkBits {
|
||||||
|
numLinks := 1 << (uint(max) - huffmanChunkBits)
|
||||||
|
h.linkMask = uint32(numLinks - 1)
|
||||||
|
|
||||||
|
// create link tables
|
||||||
|
link := nextcode[huffmanChunkBits+1] >> 1
|
||||||
|
if cap(h.links) < huffmanNumChunks-link {
|
||||||
|
h.links = make([][]uint16, huffmanNumChunks-link)
|
||||||
|
} else {
|
||||||
|
h.links = h.links[:huffmanNumChunks-link]
|
||||||
|
}
|
||||||
|
for j := uint(link); j < huffmanNumChunks; j++ {
|
||||||
|
reverse := int(bits.Reverse16(uint16(j)))
|
||||||
|
reverse >>= uint(16 - huffmanChunkBits)
|
||||||
|
off := j - uint(link)
|
||||||
|
if sanity && h.chunks[reverse] != 0 {
|
||||||
|
panic("impossible: overwriting existing chunk")
|
||||||
|
}
|
||||||
|
h.chunks[reverse] = uint16(off<<huffmanValueShift | (huffmanChunkBits + 1))
|
||||||
|
if cap(h.links[off]) < numLinks {
|
||||||
|
h.links[off] = make([]uint16, numLinks)
|
||||||
|
} else {
|
||||||
|
links := h.links[off][:0]
|
||||||
|
h.links[off] = links[:numLinks]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
h.links = h.links[:0]
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, n := range lengths {
|
||||||
|
if n == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
code := nextcode[n]
|
||||||
|
nextcode[n]++
|
||||||
|
chunk := uint16(i<<huffmanValueShift | n)
|
||||||
|
reverse := int(bits.Reverse16(uint16(code)))
|
||||||
|
reverse >>= uint(16 - n)
|
||||||
|
if n <= huffmanChunkBits {
|
||||||
|
for off := reverse; off < len(h.chunks); off += 1 << uint(n) {
|
||||||
|
// We should never need to overwrite
|
||||||
|
// an existing chunk. Also, 0 is
|
||||||
|
// never a valid chunk, because the
|
||||||
|
// lower 4 "count" bits should be
|
||||||
|
// between 1 and 15.
|
||||||
|
if sanity && h.chunks[off] != 0 {
|
||||||
|
panic("impossible: overwriting existing chunk")
|
||||||
|
}
|
||||||
|
h.chunks[off] = chunk
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
j := reverse & (huffmanNumChunks - 1)
|
||||||
|
if sanity && h.chunks[j]&huffmanCountMask != huffmanChunkBits+1 {
|
||||||
|
// Longer codes should have been
|
||||||
|
// associated with a link table above.
|
||||||
|
panic("impossible: not an indirect chunk")
|
||||||
|
}
|
||||||
|
value := h.chunks[j] >> huffmanValueShift
|
||||||
|
linktab := h.links[value]
|
||||||
|
reverse >>= huffmanChunkBits
|
||||||
|
for off := reverse; off < len(linktab); off += 1 << uint(n-huffmanChunkBits) {
|
||||||
|
if sanity && linktab[off] != 0 {
|
||||||
|
panic("impossible: overwriting existing chunk")
|
||||||
|
}
|
||||||
|
linktab[off] = chunk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if sanity {
|
||||||
|
// Above we've sanity checked that we never overwrote
|
||||||
|
// an existing entry. Here we additionally check that
|
||||||
|
// we filled the tables completely.
|
||||||
|
for i, chunk := range h.chunks {
|
||||||
|
if chunk == 0 {
|
||||||
|
// As an exception, in the degenerate
|
||||||
|
// single-code case, we allow odd
|
||||||
|
// chunks to be missing.
|
||||||
|
if code == 1 && i%2 == 1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
panic("impossible: missing chunk")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, linktab := range h.links {
|
||||||
|
for _, chunk := range linktab {
|
||||||
|
if chunk == 0 {
|
||||||
|
panic("impossible: missing chunk")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// The actual read interface needed by NewReader.
|
||||||
|
// If the passed in io.Reader does not also have ReadByte,
|
||||||
|
// the NewReader will introduce its own buffering.
|
||||||
|
type Reader interface {
|
||||||
|
io.Reader
|
||||||
|
io.ByteReader
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decompress state.
|
||||||
|
type decompressor struct {
|
||||||
|
// Input source.
|
||||||
|
r Reader
|
||||||
|
roffset int64
|
||||||
|
|
||||||
|
// Huffman decoders for literal/length, distance.
|
||||||
|
h1, h2 huffmanDecoder
|
||||||
|
|
||||||
|
// Length arrays used to define Huffman codes.
|
||||||
|
bits *[maxNumLit + maxNumDist]int
|
||||||
|
codebits *[numCodes]int
|
||||||
|
|
||||||
|
// Output history, buffer.
|
||||||
|
dict dictDecoder
|
||||||
|
|
||||||
|
// Next step in the decompression,
|
||||||
|
// and decompression state.
|
||||||
|
step func(*decompressor)
|
||||||
|
stepState int
|
||||||
|
err error
|
||||||
|
toRead []byte
|
||||||
|
hl, hd *huffmanDecoder
|
||||||
|
copyLen int
|
||||||
|
copyDist int
|
||||||
|
|
||||||
|
// Temporary buffer (avoids repeated allocation).
|
||||||
|
buf [4]byte
|
||||||
|
|
||||||
|
// Input bits, in top of b.
|
||||||
|
b uint32
|
||||||
|
|
||||||
|
nb uint
|
||||||
|
final bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *decompressor) nextBlock() {
|
||||||
|
for f.nb < 1+2 {
|
||||||
|
if f.err = f.moreBits(); f.err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f.final = f.b&1 == 1
|
||||||
|
f.b >>= 1
|
||||||
|
typ := f.b & 3
|
||||||
|
f.b >>= 2
|
||||||
|
f.nb -= 1 + 2
|
||||||
|
switch typ {
|
||||||
|
case 0:
|
||||||
|
f.dataBlock()
|
||||||
|
if debugDecode {
|
||||||
|
fmt.Println("stored block")
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
// compressed, fixed Huffman tables
|
||||||
|
f.hl = &fixedHuffmanDecoder
|
||||||
|
f.hd = nil
|
||||||
|
f.huffmanBlockDecoder()()
|
||||||
|
if debugDecode {
|
||||||
|
fmt.Println("predefinied huffman block")
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
// compressed, dynamic Huffman tables
|
||||||
|
if f.err = f.readHuffman(); f.err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
f.hl = &f.h1
|
||||||
|
f.hd = &f.h2
|
||||||
|
f.huffmanBlockDecoder()()
|
||||||
|
if debugDecode {
|
||||||
|
fmt.Println("dynamic huffman block")
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// 3 is reserved.
|
||||||
|
if debugDecode {
|
||||||
|
fmt.Println("reserved data block encountered")
|
||||||
|
}
|
||||||
|
f.err = CorruptInputError(f.roffset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *decompressor) Read(b []byte) (int, error) {
|
||||||
|
for {
|
||||||
|
if len(f.toRead) > 0 {
|
||||||
|
n := copy(b, f.toRead)
|
||||||
|
f.toRead = f.toRead[n:]
|
||||||
|
if len(f.toRead) == 0 {
|
||||||
|
return n, f.err
|
||||||
|
}
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
if f.err != nil {
|
||||||
|
return 0, f.err
|
||||||
|
}
|
||||||
|
f.step(f)
|
||||||
|
if f.err != nil && len(f.toRead) == 0 {
|
||||||
|
f.toRead = f.dict.readFlush() // Flush what's left in case of error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Support the io.WriteTo interface for io.Copy and friends.
|
||||||
|
func (f *decompressor) WriteTo(w io.Writer) (int64, error) {
|
||||||
|
total := int64(0)
|
||||||
|
flushed := false
|
||||||
|
for {
|
||||||
|
if len(f.toRead) > 0 {
|
||||||
|
n, err := w.Write(f.toRead)
|
||||||
|
total += int64(n)
|
||||||
|
if err != nil {
|
||||||
|
f.err = err
|
||||||
|
return total, err
|
||||||
|
}
|
||||||
|
if n != len(f.toRead) {
|
||||||
|
return total, io.ErrShortWrite
|
||||||
|
}
|
||||||
|
f.toRead = f.toRead[:0]
|
||||||
|
}
|
||||||
|
if f.err != nil && flushed {
|
||||||
|
if f.err == io.EOF {
|
||||||
|
return total, nil
|
||||||
|
}
|
||||||
|
return total, f.err
|
||||||
|
}
|
||||||
|
if f.err == nil {
|
||||||
|
f.step(f)
|
||||||
|
}
|
||||||
|
if len(f.toRead) == 0 && f.err != nil && !flushed {
|
||||||
|
f.toRead = f.dict.readFlush() // Flush what's left in case of error
|
||||||
|
flushed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *decompressor) Close() error {
|
||||||
|
if f.err == io.EOF {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return f.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// RFC 1951 section 3.2.7.
|
||||||
|
// Compression with dynamic Huffman codes
|
||||||
|
|
||||||
|
var codeOrder = [...]int{16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}
|
||||||
|
|
||||||
|
func (f *decompressor) readHuffman() error {
|
||||||
|
// HLIT[5], HDIST[5], HCLEN[4].
|
||||||
|
for f.nb < 5+5+4 {
|
||||||
|
if err := f.moreBits(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nlit := int(f.b&0x1F) + 257
|
||||||
|
if nlit > maxNumLit {
|
||||||
|
if debugDecode {
|
||||||
|
fmt.Println("nlit > maxNumLit", nlit)
|
||||||
|
}
|
||||||
|
return CorruptInputError(f.roffset)
|
||||||
|
}
|
||||||
|
f.b >>= 5
|
||||||
|
ndist := int(f.b&0x1F) + 1
|
||||||
|
if ndist > maxNumDist {
|
||||||
|
if debugDecode {
|
||||||
|
fmt.Println("ndist > maxNumDist", ndist)
|
||||||
|
}
|
||||||
|
return CorruptInputError(f.roffset)
|
||||||
|
}
|
||||||
|
f.b >>= 5
|
||||||
|
nclen := int(f.b&0xF) + 4
|
||||||
|
// numCodes is 19, so nclen is always valid.
|
||||||
|
f.b >>= 4
|
||||||
|
f.nb -= 5 + 5 + 4
|
||||||
|
|
||||||
|
// (HCLEN+4)*3 bits: code lengths in the magic codeOrder order.
|
||||||
|
for i := 0; i < nclen; i++ {
|
||||||
|
for f.nb < 3 {
|
||||||
|
if err := f.moreBits(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f.codebits[codeOrder[i]] = int(f.b & 0x7)
|
||||||
|
f.b >>= 3
|
||||||
|
f.nb -= 3
|
||||||
|
}
|
||||||
|
for i := nclen; i < len(codeOrder); i++ {
|
||||||
|
f.codebits[codeOrder[i]] = 0
|
||||||
|
}
|
||||||
|
if !f.h1.init(f.codebits[0:]) {
|
||||||
|
if debugDecode {
|
||||||
|
fmt.Println("init codebits failed")
|
||||||
|
}
|
||||||
|
return CorruptInputError(f.roffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HLIT + 257 code lengths, HDIST + 1 code lengths,
|
||||||
|
// using the code length Huffman code.
|
||||||
|
for i, n := 0, nlit+ndist; i < n; {
|
||||||
|
x, err := f.huffSym(&f.h1)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if x < 16 {
|
||||||
|
// Actual length.
|
||||||
|
f.bits[i] = x
|
||||||
|
i++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// Repeat previous length or zero.
|
||||||
|
var rep int
|
||||||
|
var nb uint
|
||||||
|
var b int
|
||||||
|
switch x {
|
||||||
|
default:
|
||||||
|
return InternalError("unexpected length code")
|
||||||
|
case 16:
|
||||||
|
rep = 3
|
||||||
|
nb = 2
|
||||||
|
if i == 0 {
|
||||||
|
if debugDecode {
|
||||||
|
fmt.Println("i==0")
|
||||||
|
}
|
||||||
|
return CorruptInputError(f.roffset)
|
||||||
|
}
|
||||||
|
b = f.bits[i-1]
|
||||||
|
case 17:
|
||||||
|
rep = 3
|
||||||
|
nb = 3
|
||||||
|
b = 0
|
||||||
|
case 18:
|
||||||
|
rep = 11
|
||||||
|
nb = 7
|
||||||
|
b = 0
|
||||||
|
}
|
||||||
|
for f.nb < nb {
|
||||||
|
if err := f.moreBits(); err != nil {
|
||||||
|
if debugDecode {
|
||||||
|
fmt.Println("morebits:", err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rep += int(f.b & uint32(1<<(nb®SizeMaskUint32)-1))
|
||||||
|
f.b >>= nb & regSizeMaskUint32
|
||||||
|
f.nb -= nb
|
||||||
|
if i+rep > n {
|
||||||
|
if debugDecode {
|
||||||
|
fmt.Println("i+rep > n", i, rep, n)
|
||||||
|
}
|
||||||
|
return CorruptInputError(f.roffset)
|
||||||
|
}
|
||||||
|
for j := 0; j < rep; j++ {
|
||||||
|
f.bits[i] = b
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !f.h1.init(f.bits[0:nlit]) || !f.h2.init(f.bits[nlit:nlit+ndist]) {
|
||||||
|
if debugDecode {
|
||||||
|
fmt.Println("init2 failed")
|
||||||
|
}
|
||||||
|
return CorruptInputError(f.roffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// As an optimization, we can initialize the maxRead bits to read at a time
|
||||||
|
// for the HLIT tree to the length of the EOB marker since we know that
|
||||||
|
// every block must terminate with one. This preserves the property that
|
||||||
|
// we never read any extra bytes after the end of the DEFLATE stream.
|
||||||
|
if f.h1.maxRead < f.bits[endBlockMarker] {
|
||||||
|
f.h1.maxRead = f.bits[endBlockMarker]
|
||||||
|
}
|
||||||
|
if !f.final {
|
||||||
|
// If not the final block, the smallest block possible is
|
||||||
|
// a predefined table, BTYPE=01, with a single EOB marker.
|
||||||
|
// This will take up 3 + 7 bits.
|
||||||
|
f.h1.maxRead += 10
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy a single uncompressed data block from input to output.
|
||||||
|
func (f *decompressor) dataBlock() {
|
||||||
|
// Uncompressed.
|
||||||
|
// Discard current half-byte.
|
||||||
|
left := (f.nb) & 7
|
||||||
|
f.nb -= left
|
||||||
|
f.b >>= left
|
||||||
|
|
||||||
|
offBytes := f.nb >> 3
|
||||||
|
// Unfilled values will be overwritten.
|
||||||
|
f.buf[0] = uint8(f.b)
|
||||||
|
f.buf[1] = uint8(f.b >> 8)
|
||||||
|
f.buf[2] = uint8(f.b >> 16)
|
||||||
|
f.buf[3] = uint8(f.b >> 24)
|
||||||
|
|
||||||
|
f.roffset += int64(offBytes)
|
||||||
|
f.nb, f.b = 0, 0
|
||||||
|
|
||||||
|
// Length then ones-complement of length.
|
||||||
|
nr, err := io.ReadFull(f.r, f.buf[offBytes:4])
|
||||||
|
f.roffset += int64(nr)
|
||||||
|
if err != nil {
|
||||||
|
f.err = noEOF(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
n := uint16(f.buf[0]) | uint16(f.buf[1])<<8
|
||||||
|
nn := uint16(f.buf[2]) | uint16(f.buf[3])<<8
|
||||||
|
if nn != ^n {
|
||||||
|
if debugDecode {
|
||||||
|
ncomp := ^n
|
||||||
|
fmt.Println("uint16(nn) != uint16(^n)", nn, ncomp)
|
||||||
|
}
|
||||||
|
f.err = CorruptInputError(f.roffset)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if n == 0 {
|
||||||
|
f.toRead = f.dict.readFlush()
|
||||||
|
f.finishBlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
f.copyLen = int(n)
|
||||||
|
f.copyData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// copyData copies f.copyLen bytes from the underlying reader into f.hist.
|
||||||
|
// It pauses for reads when f.hist is full.
|
||||||
|
func (f *decompressor) copyData() {
|
||||||
|
buf := f.dict.writeSlice()
|
||||||
|
if len(buf) > f.copyLen {
|
||||||
|
buf = buf[:f.copyLen]
|
||||||
|
}
|
||||||
|
|
||||||
|
cnt, err := io.ReadFull(f.r, buf)
|
||||||
|
f.roffset += int64(cnt)
|
||||||
|
f.copyLen -= cnt
|
||||||
|
f.dict.writeMark(cnt)
|
||||||
|
if err != nil {
|
||||||
|
f.err = noEOF(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.dict.availWrite() == 0 || f.copyLen > 0 {
|
||||||
|
f.toRead = f.dict.readFlush()
|
||||||
|
f.step = (*decompressor).copyData
|
||||||
|
return
|
||||||
|
}
|
||||||
|
f.finishBlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *decompressor) finishBlock() {
|
||||||
|
if f.final {
|
||||||
|
if f.dict.availRead() > 0 {
|
||||||
|
f.toRead = f.dict.readFlush()
|
||||||
|
}
|
||||||
|
f.err = io.EOF
|
||||||
|
}
|
||||||
|
f.step = (*decompressor).nextBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
// noEOF returns err, unless err == io.EOF, in which case it returns io.ErrUnexpectedEOF.
|
||||||
|
func noEOF(e error) error {
|
||||||
|
if e == io.EOF {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *decompressor) moreBits() error {
|
||||||
|
c, err := f.r.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
return noEOF(err)
|
||||||
|
}
|
||||||
|
f.roffset++
|
||||||
|
f.b |= uint32(c) << (f.nb & regSizeMaskUint32)
|
||||||
|
f.nb += 8
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the next Huffman-encoded symbol from f according to h.
|
||||||
|
func (f *decompressor) huffSym(h *huffmanDecoder) (int, error) {
|
||||||
|
// Since a huffmanDecoder can be empty or be composed of a degenerate tree
|
||||||
|
// with single element, huffSym must error on these two edge cases. In both
|
||||||
|
// cases, the chunks slice will be 0 for the invalid sequence, leading it
|
||||||
|
// satisfy the n == 0 check below.
|
||||||
|
n := uint(h.maxRead)
|
||||||
|
// Optimization. Compiler isn't smart enough to keep f.b,f.nb in registers,
|
||||||
|
// but is smart enough to keep local variables in registers, so use nb and b,
|
||||||
|
// inline call to moreBits and reassign b,nb back to f on return.
|
||||||
|
nb, b := f.nb, f.b
|
||||||
|
for {
|
||||||
|
for nb < n {
|
||||||
|
c, err := f.r.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
f.b = b
|
||||||
|
f.nb = nb
|
||||||
|
return 0, noEOF(err)
|
||||||
|
}
|
||||||
|
f.roffset++
|
||||||
|
b |= uint32(c) << (nb & regSizeMaskUint32)
|
||||||
|
nb += 8
|
||||||
|
}
|
||||||
|
chunk := h.chunks[b&(huffmanNumChunks-1)]
|
||||||
|
n = uint(chunk & huffmanCountMask)
|
||||||
|
if n > huffmanChunkBits {
|
||||||
|
chunk = h.links[chunk>>huffmanValueShift][(b>>huffmanChunkBits)&h.linkMask]
|
||||||
|
n = uint(chunk & huffmanCountMask)
|
||||||
|
}
|
||||||
|
if n <= nb {
|
||||||
|
if n == 0 {
|
||||||
|
f.b = b
|
||||||
|
f.nb = nb
|
||||||
|
if debugDecode {
|
||||||
|
fmt.Println("huffsym: n==0")
|
||||||
|
}
|
||||||
|
f.err = CorruptInputError(f.roffset)
|
||||||
|
return 0, f.err
|
||||||
|
}
|
||||||
|
f.b = b >> (n & regSizeMaskUint32)
|
||||||
|
f.nb = nb - n
|
||||||
|
return int(chunk >> huffmanValueShift), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeReader(r io.Reader) Reader {
|
||||||
|
if rr, ok := r.(Reader); ok {
|
||||||
|
return rr
|
||||||
|
}
|
||||||
|
return bufio.NewReader(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
func fixedHuffmanDecoderInit() {
|
||||||
|
fixedOnce.Do(func() {
|
||||||
|
// These come from the RFC section 3.2.6.
|
||||||
|
var bits [288]int
|
||||||
|
for i := 0; i < 144; i++ {
|
||||||
|
bits[i] = 8
|
||||||
|
}
|
||||||
|
for i := 144; i < 256; i++ {
|
||||||
|
bits[i] = 9
|
||||||
|
}
|
||||||
|
for i := 256; i < 280; i++ {
|
||||||
|
bits[i] = 7
|
||||||
|
}
|
||||||
|
for i := 280; i < 288; i++ {
|
||||||
|
bits[i] = 8
|
||||||
|
}
|
||||||
|
fixedHuffmanDecoder.init(bits[:])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *decompressor) Reset(r io.Reader, dict []byte) error {
|
||||||
|
*f = decompressor{
|
||||||
|
r: makeReader(r),
|
||||||
|
bits: f.bits,
|
||||||
|
codebits: f.codebits,
|
||||||
|
h1: f.h1,
|
||||||
|
h2: f.h2,
|
||||||
|
dict: f.dict,
|
||||||
|
step: (*decompressor).nextBlock,
|
||||||
|
}
|
||||||
|
f.dict.init(maxMatchOffset, dict)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewReader returns a new ReadCloser that can be used
|
||||||
|
// to read the uncompressed version of r.
|
||||||
|
// If r does not also implement io.ByteReader,
|
||||||
|
// the decompressor may read more data than necessary from r.
|
||||||
|
// It is the caller's responsibility to call Close on the ReadCloser
|
||||||
|
// when finished reading.
|
||||||
|
//
|
||||||
|
// The ReadCloser returned by NewReader also implements Resetter.
|
||||||
|
func NewReader(r io.Reader) io.ReadCloser {
|
||||||
|
fixedHuffmanDecoderInit()
|
||||||
|
|
||||||
|
var f decompressor
|
||||||
|
f.r = makeReader(r)
|
||||||
|
f.bits = new([maxNumLit + maxNumDist]int)
|
||||||
|
f.codebits = new([numCodes]int)
|
||||||
|
f.step = (*decompressor).nextBlock
|
||||||
|
f.dict.init(maxMatchOffset, nil)
|
||||||
|
return &f
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewReaderDict is like NewReader but initializes the reader
|
||||||
|
// with a preset dictionary. The returned Reader behaves as if
|
||||||
|
// the uncompressed data stream started with the given dictionary,
|
||||||
|
// which has already been read. NewReaderDict is typically used
|
||||||
|
// to read data compressed by NewWriterDict.
|
||||||
|
//
|
||||||
|
// The ReadCloser returned by NewReader also implements Resetter.
|
||||||
|
func NewReaderDict(r io.Reader, dict []byte) io.ReadCloser {
|
||||||
|
fixedHuffmanDecoderInit()
|
||||||
|
|
||||||
|
var f decompressor
|
||||||
|
f.r = makeReader(r)
|
||||||
|
f.bits = new([maxNumLit + maxNumDist]int)
|
||||||
|
f.codebits = new([numCodes]int)
|
||||||
|
f.step = (*decompressor).nextBlock
|
||||||
|
f.dict.init(maxMatchOffset, dict)
|
||||||
|
return &f
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user