mirror of
https://github.com/open-policy-agent/opa.git
synced 2026-08-13 03:42:35 -06:00
9cfea2c20c
Just the basic JSON types
165 lines
4.4 KiB
Plaintext
165 lines
4.4 KiB
Plaintext
{
|
|
// Command json parses JSON as defined by [1].
|
|
//
|
|
// BUGS: the escaped forward solidus (`\/`) is not currently handled for strings.
|
|
//
|
|
// TODO: check if JSON's numbers are a subset of Go's numbers, since we are
|
|
// assuming they are. Currently representing all numbers as float64.
|
|
// TODO: think about relaxing a Dictionary to allow numeric keys, as they are common in practice.
|
|
// [1]: http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
|
|
package jsonlog
|
|
|
|
const (
|
|
// note: iota is built into Go and is auto-incremented
|
|
NULL = iota
|
|
BOOLEAN = iota
|
|
NUMBER = iota
|
|
STRING = iota
|
|
VARIABLE = iota
|
|
REFERENCE = iota
|
|
ARRAY = iota
|
|
DICTIONARY = iota
|
|
)
|
|
|
|
// Location records a position in source code
|
|
type Location struct {
|
|
File string
|
|
Row int
|
|
Col int
|
|
}
|
|
|
|
func NewLocation(file string, row int, col int) *Location {
|
|
l := Location{File: file, Row: row, Col: col}
|
|
return &l
|
|
}
|
|
|
|
// Term is an argument to a function
|
|
type Term struct {
|
|
Value interface{} // actual value, as represented by Go
|
|
Kind int // type of Term: one of the consts defined above
|
|
Name []byte // original string representation
|
|
Location *Location // text location in original source
|
|
}
|
|
|
|
// NewTerm creates a new Term
|
|
func NewTerm(x interface{}, kind int, orig []byte, file string, row int, col int) *Term {
|
|
t := Term{Value: x, Kind: kind, Name: orig, Location: NewLocation(file, row, col)}
|
|
return &t
|
|
}
|
|
|
|
// Equal checks if two terms are equal
|
|
func (t1 *Term) Equal (t2 *Term) bool {
|
|
return t1.Kind == t2.Kind && t1.Value == t2.Value
|
|
}
|
|
|
|
func toIfaceSlice(v interface{}) []interface{} {
|
|
if v == nil {
|
|
return nil
|
|
}
|
|
return v.([]interface{})
|
|
}
|
|
}
|
|
|
|
Prog <- _ vals:Term+ EOF {
|
|
if vals == nil {
|
|
return make([]interface{}, 0), nil
|
|
}
|
|
return vals.([]interface{}), nil
|
|
// valsSl := toIfaceSlice(vals)
|
|
// return valsSl, nil
|
|
// switch len(valsSl) {
|
|
// case 0:
|
|
// return nil, nil
|
|
// case 1:
|
|
// return valsSl[0], nil
|
|
// default:
|
|
// return valsSl, nil
|
|
// }
|
|
}
|
|
|
|
Term <- val:( Dictionary / Array / Number / String / Bool / Null ) _ {
|
|
return val, nil
|
|
}
|
|
|
|
Dictionary <- '{' _ vals:( String _ ':' _ Value ( ',' _ String _ ':' _ Value )* )? '}' {
|
|
res := make(map[string]interface{})
|
|
valsSl := toIfaceSlice(vals)
|
|
if len(valsSl) == 0 {
|
|
return res, nil
|
|
}
|
|
res[valsSl[0].(string)] = valsSl[4]
|
|
restSl := toIfaceSlice(valsSl[5])
|
|
for _, v := range restSl {
|
|
vSl := toIfaceSlice(v)
|
|
res[vSl[2].(string)] = vSl[6]
|
|
}
|
|
t := NewTerm(res, DICTIONARY, c.text, "", c.pos.line, c.pos.col)
|
|
return t, nil //res, nil
|
|
}
|
|
|
|
Array <- '[' _ vals:( Value ( ',' _ Value )* )? ']' {
|
|
valsSl := toIfaceSlice(vals)
|
|
if len(valsSl) == 0 {
|
|
return []interface{}{}, nil
|
|
}
|
|
res := []interface{}{valsSl[0]}
|
|
restSl := toIfaceSlice(valsSl[1])
|
|
for _, v := range restSl {
|
|
vSl := toIfaceSlice(v)
|
|
res = append(res, vSl[2])
|
|
}
|
|
t := NewTerm(res, ARRAY, c.text, "", c.pos.line, c.pos.col)
|
|
return t, nil //res, nil
|
|
}
|
|
|
|
Number <- '-'? Integer ( '.' DecimalDigit+ )? Exponent? {
|
|
// JSON numbers have the same syntax as Go's, and are parseable using
|
|
// strconv.
|
|
v, err := strconv.ParseFloat(string(c.text), 64)
|
|
t := NewTerm(v, NUMBER, c.text, "", c.pos.line, c.pos.col)
|
|
return t, err
|
|
}
|
|
|
|
Integer <- '0' / NonZeroDecimalDigit DecimalDigit*
|
|
|
|
Exponent <- 'e'i [+-]? DecimalDigit+
|
|
|
|
String <- '"' ( !EscapedChar . / '\\' EscapeSequence )* '"' {
|
|
// TODO : the forward slash (solidus) is not a valid escape in Go, it will
|
|
// fail if there's one in the string
|
|
v, err := strconv.Unquote(string(c.text))
|
|
t := NewTerm(v, STRING, c.text, "", c.pos.line, c.pos.col)
|
|
return t, err // v, err
|
|
}
|
|
|
|
EscapedChar <- [\x00-\x1f"\\]
|
|
|
|
EscapeSequence <- SingleCharEscape / UnicodeEscape
|
|
|
|
SingleCharEscape <- ["\\/bfnrt]
|
|
|
|
UnicodeEscape <- 'u' HexDigit HexDigit HexDigit HexDigit
|
|
|
|
DecimalDigit <- [0-9]
|
|
|
|
NonZeroDecimalDigit <- [1-9]
|
|
|
|
HexDigit <- [0-9a-f]i
|
|
|
|
Bool <- "true" {
|
|
t := NewTerm(true, BOOLEAN, c.text, "", c.pos.line, c.pos.col)
|
|
return t, nil // true, nil
|
|
} / "false" {
|
|
t := NewTerm(false, BOOLEAN, c.text, "", c.pos.line, c.pos.col)
|
|
return t, nil // false, nil
|
|
}
|
|
|
|
Null <- "null" {
|
|
t := NewTerm(nil, NULL, c.text, "", c.pos.line, c.pos.col)
|
|
return t, nil //nil, nil
|
|
}
|
|
|
|
_ "whitespace" <- [ \t\r\n]*
|
|
|
|
EOF <- !.
|