mirror of
https://github.com/google/go-jsonnet.git
synced 2025-09-30 01:41:04 +02:00
Check in work in progress on parser. Doesn't build right now.
This commit is contained in:
parent
dc07929f9a
commit
7b6ac5f2d7
5
ast.go
5
ast.go
@ -235,7 +235,10 @@ type astDollar struct{ astNodeBase }
|
|||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
// astError represents the error e.
|
// astError represents the error e.
|
||||||
type astError struct{ astNodeBase }
|
type astError struct {
|
||||||
|
astNodeBase
|
||||||
|
expr astNode
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
73
lexer.go
73
lexer.go
@ -35,10 +35,8 @@ type fodder []fodderElement
|
|||||||
type tokenKind int
|
type tokenKind int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
tokenInvalid tokenKind = iota
|
|
||||||
|
|
||||||
// Symbols
|
// Symbols
|
||||||
tokenBraceL
|
tokenBraceL tokenKind = iota
|
||||||
tokenBraceR
|
tokenBraceR
|
||||||
tokenBracketL
|
tokenBracketL
|
||||||
tokenBracketR
|
tokenBracketR
|
||||||
@ -54,9 +52,9 @@ const (
|
|||||||
tokenIdentifier
|
tokenIdentifier
|
||||||
tokenNumber
|
tokenNumber
|
||||||
tokenOperator
|
tokenOperator
|
||||||
|
tokenStringBlock
|
||||||
tokenStringDouble
|
tokenStringDouble
|
||||||
tokenStringSingle
|
tokenStringSingle
|
||||||
tokenStringBlock
|
|
||||||
|
|
||||||
// Keywords
|
// Keywords
|
||||||
tokenAssert
|
tokenAssert
|
||||||
@ -71,10 +69,10 @@ const (
|
|||||||
tokenIn
|
tokenIn
|
||||||
tokenLocal
|
tokenLocal
|
||||||
tokenNullLit
|
tokenNullLit
|
||||||
tokenTailStrict
|
|
||||||
tokenThen
|
|
||||||
tokenSelf
|
tokenSelf
|
||||||
tokenSuper
|
tokenSuper
|
||||||
|
tokenTailStrict
|
||||||
|
tokenThen
|
||||||
tokenTrue
|
tokenTrue
|
||||||
|
|
||||||
// A special token that holds line/column information about the end of the
|
// A special token that holds line/column information about the end of the
|
||||||
@ -82,6 +80,59 @@ const (
|
|||||||
tokenEndOfFile
|
tokenEndOfFile
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var tokenKindStrings = []string{
|
||||||
|
// Symbols
|
||||||
|
tokenBraceL: "\"{\"",
|
||||||
|
tokenBraceR: "\"}\"",
|
||||||
|
tokenBracketL: "\"[\"",
|
||||||
|
tokenBracketR: "\"]\"",
|
||||||
|
tokenColon: "\":\"",
|
||||||
|
tokenComma: "\",\"",
|
||||||
|
tokenDollar: "\"$\"",
|
||||||
|
tokenDot: "\".\"",
|
||||||
|
tokenParenL: "\"(\"",
|
||||||
|
tokenParenR: "\")\"",
|
||||||
|
tokenSemicolon: "\";\"",
|
||||||
|
|
||||||
|
// Arbitrary length lexemes
|
||||||
|
tokenIdentifier: "IDENTIFIER",
|
||||||
|
tokenNumber: "NUMBER",
|
||||||
|
tokenOperator: "OPERATOR",
|
||||||
|
tokenStringBlock: "STRING_BLOCK",
|
||||||
|
tokenStringDouble: "STRING_DOUBLE",
|
||||||
|
tokenStringSingle: "STRING_SINGLE",
|
||||||
|
|
||||||
|
// Keywords
|
||||||
|
tokenAssert: "assert",
|
||||||
|
tokenElse: "else",
|
||||||
|
tokenError: "error",
|
||||||
|
tokenFalse: "false",
|
||||||
|
tokenFor: "for",
|
||||||
|
tokenFunction: "function",
|
||||||
|
tokenIf: "if",
|
||||||
|
tokenImport: "import",
|
||||||
|
tokenImportStr: "importstr",
|
||||||
|
tokenIn: "in",
|
||||||
|
tokenLocal: "local",
|
||||||
|
tokenNullLit: "null",
|
||||||
|
tokenSelf: "self",
|
||||||
|
tokenSuper: "super",
|
||||||
|
tokenTailStrict: "tailstrict",
|
||||||
|
tokenThen: "then",
|
||||||
|
tokenTrue: "true",
|
||||||
|
|
||||||
|
// A special token that holds line/column information about the end of the
|
||||||
|
// file.
|
||||||
|
tokenEndOfFile: "end of file",
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tk tokenKind) String() string {
|
||||||
|
if tk < 0 || int(tk) >= len(tokenKindStrings) {
|
||||||
|
panic(fmt.Sprintf("INTERNAL ERROR: Unknown token kind:: %v", tk))
|
||||||
|
}
|
||||||
|
return tokenKindStrings[tk]
|
||||||
|
}
|
||||||
|
|
||||||
type token struct {
|
type token struct {
|
||||||
kind tokenKind // The type of the token
|
kind tokenKind // The type of the token
|
||||||
fodder fodder // Any fodder the occurs before this token
|
fodder fodder // Any fodder the occurs before this token
|
||||||
@ -96,6 +147,16 @@ type token struct {
|
|||||||
|
|
||||||
type tokens []token
|
type tokens []token
|
||||||
|
|
||||||
|
func (t *token) String() string {
|
||||||
|
if t.data == "" {
|
||||||
|
return t.kind.String()
|
||||||
|
} else if t.kind == tokenOperator {
|
||||||
|
return fmt.Sprintf("\"%v\"", t.data)
|
||||||
|
} else {
|
||||||
|
return fmt.Sprintf("(%v, \"%v\")", t.kind, t.data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Helpers
|
// Helpers
|
||||||
|
|
||||||
|
207
parser.go
Normal file
207
parser.go
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
package jsonnet
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type precedence int
|
||||||
|
|
||||||
|
const (
|
||||||
|
applyPrecedence precedence = 2 // Function calls and indexing.
|
||||||
|
unaryPrecedence precedence = 4 // Logical and bitwise negation, unary + -
|
||||||
|
beforeElsePrecedence precedence = 15 // True branch of an if.
|
||||||
|
maxPrecedence precedence = 16 // Local, If, Import, Function, Error
|
||||||
|
)
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func makeUnexpectedError(t token, while string) error {
|
||||||
|
return makeStaticError(
|
||||||
|
fmt.Sprintf("Unexpected: %v while %v", t, while), t.loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
func locFromTokens(begin, end *token) LocationRange {
|
||||||
|
return makeLocationRange(begin.loc.FileName, begin.loc.Begin, end.loc.End)
|
||||||
|
}
|
||||||
|
|
||||||
|
func locFromTokenAST(begin *token, end astNode) LocationRange {
|
||||||
|
return makeLocationRange(begin.loc.FileName, begin.loc.Begin, end.Loc().End)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
type parser struct {
|
||||||
|
t tokens
|
||||||
|
currT int
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeParser(t tokens) *parser {
|
||||||
|
return &parser{
|
||||||
|
t: t,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) pop() *token {
|
||||||
|
t := &p.t[p.currT]
|
||||||
|
p.currT++
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) popExpect(tk tokenKind) (*token, error) {
|
||||||
|
t := p.pop()
|
||||||
|
if t.kind != tk {
|
||||||
|
return nil, makeStaticError(
|
||||||
|
fmt.Sprintf("Expected token %v but got %v", tk, t), t.loc)
|
||||||
|
}
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) popExpectOp(op string) (*token, error) {
|
||||||
|
t := p.pop()
|
||||||
|
if t.kind != tokenOperator || t.data != op {
|
||||||
|
return nil, makeStaticError(
|
||||||
|
fmt.Sprintf("Expected operator %v but got %v", op, t), t.loc)
|
||||||
|
}
|
||||||
|
return t, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) peek() *token {
|
||||||
|
return &p.t[p.currT]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) parseIdentifierList(elementKind string) (identifiers, bool, error) {
|
||||||
|
exprs, got_comma, err := p.parseCommaList(tokenParenR, elementKind)
|
||||||
|
if err != nil {
|
||||||
|
return identifiers{}, false, err
|
||||||
|
}
|
||||||
|
var ids identifiers
|
||||||
|
for n := range exprs {
|
||||||
|
v, ok := n.(astVar)
|
||||||
|
if !ok {
|
||||||
|
return identifiers{}, false, makeStaticError(fmt.Sprintf("Not an identifier: %v", n), n.Loc())
|
||||||
|
}
|
||||||
|
ids = append(ids, v.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) parseCommaList(tokenKind end, elementkind string) (astNodes, bool, error) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *parser) parse(prec precedence) (astNode, error) {
|
||||||
|
begin := p.peek()
|
||||||
|
|
||||||
|
switch begin.kind {
|
||||||
|
// These cases have effectively maxPrecedence as the first
|
||||||
|
// call to parse will parse them.
|
||||||
|
case tokenAssert:
|
||||||
|
p.pop()
|
||||||
|
cond, err := p.parse(maxPrecedence)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var msg astNode
|
||||||
|
if p.peek().kind == tokenColon {
|
||||||
|
p.pop()
|
||||||
|
msg, err = p.parse(maxPrecedence)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, err = p.popExpect(tokenSemicolon)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
rest, err := p.parse(maxPrecedence)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &astAssert{
|
||||||
|
astNodeBase: astNodeBase{locFromTokenAST(begin, rest)},
|
||||||
|
cond: cond,
|
||||||
|
message: msg,
|
||||||
|
rest: rest,
|
||||||
|
}, nil
|
||||||
|
|
||||||
|
case tokenError:
|
||||||
|
p.pop()
|
||||||
|
expr, err := p.parse(maxPrecedence)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &astError{
|
||||||
|
astNodeBase: astNodeBase{locFromTokenAST(begin, expr)},
|
||||||
|
expr: expr,
|
||||||
|
}, nil
|
||||||
|
|
||||||
|
case tokenIf:
|
||||||
|
p.pop()
|
||||||
|
cond, err := p.parse(maxPrecedence)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_, err = p.popExpect(tokenThen)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
branchTrue, err := p.parse(maxPrecedence)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var branchFalse astNode
|
||||||
|
lr := locFromTokenAST(begin, branchTrue)
|
||||||
|
if p.peek().kind == tokenElse {
|
||||||
|
p.pop()
|
||||||
|
branchFalse, err = p.parse(maxPrecedence)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
lr = locFromTokenAST(begin, branchFalse)
|
||||||
|
}
|
||||||
|
return &astConditional{
|
||||||
|
astNodeBase: astNodeBase{lr},
|
||||||
|
cond: cond,
|
||||||
|
branchTrue: branchTrue,
|
||||||
|
branchFalse: branchFalse,
|
||||||
|
}, nil
|
||||||
|
case tokenFunction:
|
||||||
|
p.pop()
|
||||||
|
next := p.pop()
|
||||||
|
if next.kind == tokenParenL {
|
||||||
|
params, got_comma, err := p.parseIdentifierList("function parameter")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
body, err := p.parse(maxPrecedence)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return astFunction{
|
||||||
|
astNodeBase: astNodeBase{locFromTokenAST(begin, body)},
|
||||||
|
parameters: params,
|
||||||
|
trailingComma, got_comma,
|
||||||
|
body: body,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return makeStaticError(fmt.Sprintf("Expected ( but got %v", next), next.loc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func parse(t tokens) (astNode, error) {
|
||||||
|
p := makeParser(t)
|
||||||
|
expr, err := p.parse(maxPrecedence)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.peek().kind != tokenEndOfFile {
|
||||||
|
return nil, makeStaticError(fmt.Sprintf("Did not expect: %v", p.peek()), p.peek().loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
return expr, nil
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user