From 96812b9ea732cc7ae26efce4568c19aec0000abc Mon Sep 17 00:00:00 2001 From: Charlie Stanton Date: Wed, 21 Sep 2022 19:37:02 +0100 Subject: Adds some new commands --- main/parse.go | 103 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 98 insertions(+), 5 deletions(-) (limited to 'main/parse.go') 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 { -- cgit v1.2.3