<- Back to shtanton's homepage
aboutsummaryrefslogtreecommitdiff
path: root/main/parse.go
diff options
context:
space:
mode:
authorCharlie Stanton <charlie@shtanton.xyz>2022-09-21 19:37:02 +0100
committerCharlie Stanton <charlie@shtanton.xyz>2022-09-21 19:37:02 +0100
commit96812b9ea732cc7ae26efce4568c19aec0000abc (patch)
tree7a5f55230ac15648c6b66dfc0870e19a1c12a78b /main/parse.go
parentcfbe645715114234510bda068d2f30ffbe208eae (diff)
downloadstred-go-96812b9ea732cc7ae26efce4568c19aec0000abc.tar
Adds some new commands
Diffstat (limited to 'main/parse.go')
-rw-r--r--main/parse.go103
1 files changed, 98 insertions, 5 deletions
diff --git a/main/parse.go b/main/parse.go
index 0767c0d..5466a02 100644
--- a/main/parse.go
+++ b/main/parse.go
@@ -37,7 +37,7 @@ var segmentTokens map[TokenType]bool = map[TokenType]bool {
TokenHash: true,
TokenAt: true,
TokenDot: true,
- TokenLParen: true,
+ TokenLBrack: true,
}
func (p *parser) parsePathPatternFilter(minPower int) PathFilterAST {
@@ -62,10 +62,10 @@ func (p *parser) parsePathPatternFilter(minPower int) PathFilterAST {
lhs = IntegerSegmentPathFilterAST{index}
case TokenDot:
lhs = AnySegmentPathFilterAST{}
- case TokenLParen:
+ case TokenLBrack:
lhs = p.parsePathPatternFilter(0)
- if p.next().typ != TokenRParen {
- panic("Expected )")
+ if p.next().typ != TokenRBrack {
+ panic("Expected ] in path filter")
}
default:
panic("Expected path pattern filter segment")
@@ -94,7 +94,7 @@ func (p *parser) parseFilter(minPower int) Filter {
var lhs Filter
token := p.next()
switch token.typ {
- case TokenHash, TokenAt, TokenDot:
+ case TokenHash, TokenAt, TokenDot, TokenLBrack:
p.rewind(token)
filterAst := p.parsePathPatternFilter(0)
lhs = compilePathFilterAST(filterAst)
@@ -106,6 +106,12 @@ func (p *parser) parseFilter(minPower int) Filter {
lhs = TerminalFilter{}
case TokenTilde:
lhs = RootFilter{}
+ case TokenLParen:
+ lhs = p.parseFilter(0)
+ rParen := p.next()
+ if rParen.typ != TokenRParen {
+ panic("Missing ) in filter")
+ }
default:
panic("Expected filter")
}
@@ -114,6 +120,8 @@ func (p *parser) parseFilter(minPower int) Filter {
switch {
case token.typ == TokenAnd && 2 >= minPower:
lhs = AndFilter {lhs, p.parseFilter(3)}
+ case token.typ == TokenOr && 0 >= minPower:
+ lhs = OrFilter {lhs, p.parseFilter(1)}
default:
p.rewind(token)
break loop
@@ -122,12 +130,90 @@ func (p *parser) parseFilter(minPower int) Filter {
return lhs
}
+func (p *parser) parseLiterals() (items []WalkItem) {
+ var path Path
+ var value WalkValue
+ loop: for {
+ token := p.next()
+ switch token.typ {
+ case TokenSemicolon, TokenEOF:
+ p.rewind(token)
+ break loop
+ case TokenComma:
+ case TokenNullLiteral:
+ value = ValueNull{}
+ case TokenTrueLiteral:
+ value = ValueBool(true)
+ case TokenFalseLiteral:
+ value = ValueBool(false)
+ case TokenNumberLiteral:
+ numberLiteral, err := strconv.ParseFloat(token.val, 64)
+ if err != nil {
+ panic("Error parsing number literal to float64")
+ }
+ value = ValueNumber(numberLiteral)
+ case TokenDoubleQuote:
+ stringToken := p.next()
+ if stringToken.typ != TokenStringLiteral {
+ panic("Expected string literal after \"")
+ }
+ // TODO: resolve escape characters
+ stringLiteral := stringToken.val
+ if p.next().typ != TokenDoubleQuote {
+ panic("Expected \" after string literal")
+ }
+ colon := p.next()
+ if colon.typ == TokenColon {
+ if path != nil {
+ panic("Expected value after path:")
+ }
+ path = Path{stringLiteral}
+ } else {
+ p.rewind(colon)
+ value = ValueString(stringLiteral)
+ }
+ case TokenTerminalLiteral:
+ switch token.val {
+ case "{":
+ value = MapBegin
+ case "}":
+ value = MapEnd
+ case "[":
+ value = ArrayBegin
+ case "]":
+ value = ArrayEnd
+ default:
+ panic("Invalid terminal token")
+ }
+ }
+ if value != nil {
+ items = append(items, WalkItem {
+ path: path,
+ value: value,
+ })
+ path = nil
+ value = nil
+ }
+ }
+ if path != nil {
+ panic("Expected value after path:")
+ }
+ return items
+}
+
func (p *parser) parseBasicCommand(commandChar rune) Command {
switch commandChar {
case 'p':
return PrintValueCommand{}
case 'd':
return DeleteAllCommand{}
+ case 'n':
+ return NextCommand{}
+ case 'N':
+ return AppendNextCommand{}
+ case 'i':
+ items := p.parseLiterals()
+ return PrintLiteralsCommand {items: items}
default:
panic("Invalid command")
}
@@ -151,6 +237,12 @@ func (p *parser) parseCommand() Command {
command: command,
}
return command
+ case TokenLBrace:
+ commands := p.parseCommands()
+ if p.next().typ != TokenRBrace {
+ panic("Missing matching }")
+ }
+ return SequenceCommand {commands}
case TokenCommand:
commandChar, _, err := strings.NewReader(token.val).ReadRune()
if err != nil {
@@ -172,6 +264,7 @@ func (p *parser) parseCommands() []Command {
commands = append(commands, p.parseCommand())
semicolon := p.next()
if semicolon.typ == TokenEOF || semicolon.typ == TokenRBrace {
+ p.rewind(semicolon)
return commands
}
if semicolon.typ != TokenSemicolon {