Signal ReadString(ref Readable <char> @in, out Tokenized @out) { int start = @in.Offset; for (; [email protected]; @in.Move()) { switch (@in.Peek) { //below should do another move(), but checking to see if empty... case '\\': //BUT! what about "\\", eh??? need to lookahead to know what to do @in.Move(); //which we can't do, as we might be at end of buffer break; //if we're at end, then, what? it's like we want to look enter a special mode when we get here //or - maybe we have to emit a StringPart and start again case '"': int length = @in.Offset - start; @in.Move(); Pop(); return(Emit(out @out, start, length, Token.String)); } } //x.Emit(Token.StringPart, 0, i); //but only if i > 1...! return(Underrun(out @out)); }
Signal ReadNumber(ref Readable <char> @in, out Tokenized @out) { int start = @in.Offset; @in.Move(); for (; [email protected]; @in.Move()) { if (!IsNumeric(@in.Peek)) { Pop(); int len = @in.Offset - start; return(Emit(out @out, start, len, Token.Number)); } } return(Underrun(out @out)); }
public Signal Next(ref Readable <char> @in, out Tokenized @out) { start: if (@in.IsEmpty) //should just try reading, surely... { return(Underrun(out @out)); } char current = @in.Peek; if (_mode != Mode.String && IsWhitespace(current)) { SkipWhitespace(ref @in); goto start; } //stupid edge case //of line feed without the carriage return //requires look-ahead switch (_mode) { case Mode.Line: if (current == 0) //shouldn't we just, you know, return the signal here... { @in.Move(); return(End(out @out)); } else { Push(Mode.LineEnd); Switch(Mode.Value); goto start; } case Mode.LineEnd: if (current == 0) { @in.Move(); return(End(out @out)); } throw new NotImplementedException("Handle line break?"); case Mode.Value: switch (current) { case '"': @in.Move(); Switch(Mode.String); goto start; case '{': @in.Move(); Switch(Mode.Object1); return(Emit(out @out, 1, 0, Token.Object)); case '[': @in.Move(); Switch(Mode.Array1); return(Emit(out @out, 1, 0, Token.Array)); case char c when IsNumeric(c): return(ReadNumber(ref @in, out @out)); } break; case Mode.Object1: switch (current) { case '}': @in.Move(); Pop(); return(Emit(out @out, 1, 0, Token.ObjectEnd)); case '"': @in.Move(); Push(Mode.Object2); Switch(Mode.String); goto start; } break; case Mode.Object2: switch (current) { case ':': @in.Move(); Push(Mode.Object3); Switch(Mode.Value); goto start; } break; case Mode.Object3: switch (current) { case ',': @in.Move(); Switch(Mode.Object1); goto start; case '}': @in.Move(); Pop(); return(Emit(out @out, 1, 0, Token.ObjectEnd)); } break; case Mode.Array1: switch (current) { case ']': @in.Move(); Pop(); return(Emit(out @out, 1, 0, Token.ArrayEnd)); default: Push(Mode.Array2); Switch(Mode.Value); goto start; } case Mode.Array2: switch (current) { case ']': @in.Move(); Pop(); return(Emit(out @out, 1, 0, Token.ArrayEnd)); case ',': @in.Move(); Switch(Mode.Array1); goto start; } break; case Mode.String: return(ReadString(ref @in, out @out)); } return(BadInput(out @out)); }
static Signal Return(out Tokenized @out, Signal status) { @out = default; return(status); }
static Signal BadInput(out Tokenized @out) => Return(out @out, Signal.BadInput);
static Signal End(out Tokenized @out) => Return(out @out, Signal.End);
static Signal Underrun(out Tokenized @out) => Return(out @out, Signal.Underrun);
=> c == ' '; //more to add! // Result Ok(out Tokenized @out, int chars = 0) { // _offset += chars; // @out = default; //but there needs to be some way of signalling that there's no token returned... BUT why are we even yielding, if there's no token, and no signal? // return (Status.Ok, chars); // } Signal Emit(out Tokenized @out, int before, int length, Token token) { @out = new Tokenized(before, length, token); return(Signal.Ok); }