Check in work in progress on parser. Doesn't build right now.

This commit is contained in:
Joe Beda 2016-02-16 10:46:31 -08:00
parent dc07929f9a
commit 7b6ac5f2d7
3 changed files with 278 additions and 7 deletions

5
ast.go
View File

@ -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
}
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------

View File

@ -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
View 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
}