diff options
author | Charlie Stanton <charlie@shtanton.xyz> | 2022-09-21 19:37:02 +0100 |
---|---|---|
committer | Charlie Stanton <charlie@shtanton.xyz> | 2022-09-21 19:37:02 +0100 |
commit | 96812b9ea732cc7ae26efce4568c19aec0000abc (patch) | |
tree | 7a5f55230ac15648c6b66dfc0870e19a1c12a78b /main/lex.go | |
parent | cfbe645715114234510bda068d2f30ffbe208eae (diff) | |
download | stred-go-96812b9ea732cc7ae26efce4568c19aec0000abc.tar |
Adds some new commands
Diffstat (limited to 'main/lex.go')
-rw-r--r-- | main/lex.go | 194 |
1 files changed, 187 insertions, 7 deletions
diff --git a/main/lex.go b/main/lex.go index 91231ed..0daf2d1 100644 --- a/main/lex.go +++ b/main/lex.go @@ -64,6 +64,16 @@ func (l *lexer) reset() { l.pos = l.start } +func (l *lexer) expect(valid string) bool { + for _, r := range valid { + if l.next() != r { + l.backup() + return false + } + } + return true +} + func (l *lexer) peek() rune { w := l.width r := l.next() @@ -116,6 +126,7 @@ const ( TokenDot // . TokenAst // * TokenBar // | + TokenOr // || TokenAnd // && TokenHat // ^ TokenDollar // $ @@ -123,6 +134,17 @@ const ( TokenHatDollar // ^$ TokenExclamation // ! TokenTilde // ~ + TokenDoubleQuote // " + TokenStringLiteral // A string literal, not including the " either side + TokenNullLiteral // null + TokenTrueLiteral // true + TokenFalseLiteral // false + TokenColon // : + TokenComma // , + TokenSubstituteDelimiter // usually / but could be something else + TokenSubstitutePlaceholder // \1, \2 etc. + TokenTerminalLiteral // One of {, }, [, ] + TokenNumberLiteral // A number literal TokenPatternStringIndex // A string index in a pattern TokenPatternIntegerIndex // An integer index in a pattern ) @@ -183,10 +205,12 @@ func lexCommand(l *lexer) stateFunc { switch r { case '#': l.emit(TokenHash) - return lexPatternStringIndex + lexPatternStringIndex(l) + return lexCommand case '@': l.emit(TokenAt) - return lexPatternIntegerIndex + lexPatternIntegerIndex(l) + return lexCommand case '.': l.emit(TokenDot) return lexCommand @@ -194,7 +218,17 @@ func lexCommand(l *lexer) stateFunc { l.emit(TokenAst) return lexCommand case '|': - l.emit(TokenBar) + if l.accept("|") { + l.emit(TokenOr) + } else { + l.emit(TokenBar) + } + return lexCommand + case '[': + l.emit(TokenLBrack) + return lexCommand + case ']': + l.emit(TokenRBrack) return lexCommand case '(': l.emit(TokenLParen) @@ -232,6 +266,12 @@ func lexCommand(l *lexer) stateFunc { case '~': l.emit(TokenTilde) return lexCommand + case 'i': + l.emit(TokenCommand) + return lexMultipleLiterals + case 'S': + l.emit(TokenCommand) + return lexBigSubstitution } if isAlpha(r) { l.emit(TokenCommand) @@ -240,16 +280,152 @@ func lexCommand(l *lexer) stateFunc { return l.errorf("Expected command found something else") } -func lexPatternStringIndex(l *lexer) stateFunc { +func lexBigSubstitution(l *lexer) stateFunc { + delimiter := l.next() + if delimiter == eof || isAlphaNumeric(delimiter) { + return l.errorf("Invalid delimiter for big substitution") + } + l.emit(TokenSubstituteDelimiter) + loop: for { + r := l.next() + switch r { + case delimiter: + l.emit(TokenSubstituteDelimiter) + break loop + case '#': + l.emit(TokenHash) + lexPatternStringIndex(l) + case '@': + l.emit(TokenAt) + lexPatternIntegerIndex(l) + case '.': + l.emit(TokenDot) + case '*': + l.emit(TokenAst) + case '|': + l.emit(TokenBar) + case '[': + l.emit(TokenLBrack) + case ']': + l.emit(TokenRBrack) + case '?': + l.emit(TokenQuestion) + case ':': + l.emit(TokenColon) + case ',': + l.emit(TokenComma) + } + } + loop2: for { + r := l.next() + switch r { + case delimiter: + l.emit(TokenSubstituteDelimiter) + break loop2 + case '\\': + if !l.acceptPassing(isDigit) { + return l.errorf("Expected digit after \\") + } + l.emit(TokenSubstitutePlaceholder) + } + } + // TODO: No clue where I was going with this + return lexCommand +} + +func lexMultipleLiterals(l *lexer) stateFunc { + l.acceptAll(whitespaceNewlines) + l.ignore() + r := l.next() + switch r { + case ';', eof: + l.backup() + return lexCommandEnd + case ':': + l.emit(TokenColon) + return lexMultipleLiterals + case ',': + l.emit(TokenComma) + return lexMultipleLiterals + } + err := lexSingleLiteral(l) + if err != "" { + return l.errorf(err) + } + return lexMultipleLiterals +} + +func lexSingleLiteral(l *lexer) string { + l.acceptAll(whitespaceNewlines) + l.ignore() + r := l.next() + switch r { + case '"': + l.emit(TokenDoubleQuote) + if !lexStringLiteral(l) { + return "Expected closing \"" + } + case 'n': + if !l.expect("ull") { + return "Invalid literal, expected null" + } + l.emit(TokenNullLiteral) + case 't': + if !l.expect("rue") { + return "Invalid literal, expected true" + } + l.emit(TokenTrueLiteral) + case 'f': + if !l.expect("alse") { + return "Invalid literal, expected false" + } + l.emit(TokenFalseLiteral) + case '{', '}', '[', ']': + l.emit(TokenTerminalLiteral) + default: + if isDigit(r) { + lexNumberLiteral(l) + return "" + } + return "Invalid literal" + } + return "" +} + +// Just read the first digit +func lexNumberLiteral(l *lexer) { + l.acceptAllPassing(isDigit) + if l.accept(".") { + l.acceptAllPassing(isDigit) + } + l.emit(TokenNumberLiteral) +} + +// TODO: escape characters +func lexStringLiteral(l *lexer) bool { + for { + r := l.next() + switch r { + case '"': + l.backup() + l.emit(TokenStringLiteral) + l.next() + l.emit(TokenDoubleQuote) + return true + case eof: + return false + } + } +} + +func lexPatternStringIndex(l *lexer) { l.acceptAllPassing(isStringIndexChar) l.emit(TokenPatternStringIndex) - return lexCommand } -func lexPatternIntegerIndex(l *lexer) stateFunc { +func lexPatternIntegerIndex(l *lexer) { l.acceptAllPassing(isDigit) l.emit(TokenPatternIntegerIndex) - return lexCommand } func lexCommandEnd(l *lexer) stateFunc { @@ -261,5 +437,9 @@ func lexCommandEnd(l *lexer) stateFunc { l.emit(TokenSemicolon) return lexCommand } + if l.accept("}") { + l.emit(TokenRBrace) + return lexCommandEnd + } return l.errorf("Expected ; found something else") } |