mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-28 17:04:41 +00:00
For `nim secret`: - **fix(repl): eof(ctrl-D/Z) and ctrl-C were ignored** - **feat(repl): continueLine figures section, constr, bool ops** --------- Co-authored-by: ringabout <43030857+ringabout@users.noreply.github.com>
265 lines
7.2 KiB
Nim
265 lines
7.2 KiB
Nim
#
|
|
#
|
|
# The Nim Compiler
|
|
# (c) Copyright 2012 Andreas Rumpf
|
|
#
|
|
# See the file "copying.txt", included in this
|
|
# distribution, for details about the copyright.
|
|
#
|
|
|
|
## Low-level streams for high performance.
|
|
|
|
import
|
|
pathutils
|
|
import std/strutils
|
|
when defined(nimPreviewSlimSystem):
|
|
import std/syncio
|
|
|
|
# support `useGnuReadline`, `useLinenoise` for backwards compatibility
|
|
const hasRstdin = (defined(nimUseLinenoise) or defined(useLinenoise) or defined(useGnuReadline)) and
|
|
not defined(windows)
|
|
|
|
when hasRstdin: import std/rdstdin
|
|
|
|
type
|
|
TLLRepl* = proc (s: PLLStream, buf: pointer, bufLen: int): int
|
|
OnPrompt* = proc() {.closure.}
|
|
TLLStreamKind* = enum # enum of different stream implementations
|
|
llsNone, # null stream: reading and writing has no effect
|
|
llsString, # stream encapsulates a string
|
|
llsFile, # stream encapsulates a file
|
|
llsStdIn # stream encapsulates stdin
|
|
TLLStream* = object of RootObj
|
|
kind*: TLLStreamKind # accessible for low-level access (lexbase uses this)
|
|
f*: File
|
|
s*: string
|
|
rd*, wr*: int # for string streams
|
|
lineOffset*: int # for fake stdin line numbers
|
|
repl*: TLLRepl # gives stdin control to clients
|
|
onPrompt*: OnPrompt
|
|
|
|
PLLStream* = ref TLLStream
|
|
|
|
proc llStreamOpen*(data: sink string): PLLStream =
|
|
PLLStream(kind: llsString, s: data)
|
|
|
|
proc llStreamOpen*(f: File): PLLStream =
|
|
PLLStream(kind: llsFile, f: f)
|
|
|
|
proc llStreamOpen*(filename: AbsoluteFile, mode: FileMode): PLLStream =
|
|
result = PLLStream(kind: llsFile)
|
|
if not open(result.f, filename.string, mode): result = nil
|
|
|
|
proc llStreamOpen*(): PLLStream =
|
|
PLLStream(kind: llsNone)
|
|
|
|
proc llReadFromStdin(s: PLLStream, buf: pointer, bufLen: int): int
|
|
proc llStreamOpenStdIn*(r: TLLRepl = llReadFromStdin, onPrompt: OnPrompt = nil): PLLStream =
|
|
PLLStream(kind: llsStdIn, s: "", lineOffset: -1, repl: r, onPrompt: onPrompt)
|
|
|
|
proc llStreamClose*(s: PLLStream) =
|
|
case s.kind
|
|
of llsNone, llsString, llsStdIn:
|
|
discard
|
|
of llsFile:
|
|
close(s.f)
|
|
|
|
when not declared(readLineFromStdin):
|
|
# fallback implementation:
|
|
proc readLineFromStdin(prompt: string, line: var string): bool =
|
|
stdout.write(prompt)
|
|
stdout.flushFile()
|
|
result = readLine(stdin, line)
|
|
if not result:
|
|
stdout.write("\n")
|
|
quit(0)
|
|
|
|
proc endsWith*(x: string, s: set[char]): bool =
|
|
var i = x.len-1
|
|
while i >= 0 and x[i] == ' ': dec(i)
|
|
if i >= 0 and x[i] in s:
|
|
result = true
|
|
else:
|
|
result = false
|
|
|
|
const
|
|
LineContinuationOprs = {'+', '-', '*', '/', '\\', '<', '>', '!', '?', '^',
|
|
'|', '%', '&', '$', '@', '~', ','}
|
|
AdditionalLineContinuationOprs = {'#', ':', '='}
|
|
LineContinuationTokens = [
|
|
"let", "var", "const", "type", # section
|
|
"object", "tuple",
|
|
# from ./layouter.oprSet
|
|
"div", "mod", "shl", "shr", "in", "notin", "is",
|
|
"isnot", "not", "of", "as", "from", "..", "and", "or", "xor",
|
|
] # must be all `nimIdentNormalized`-ed
|
|
|
|
proc eqIdent(a, bNormalized: string): bool =
|
|
a.nimIdentNormalize == bNormalized
|
|
|
|
proc endsWithIdent(s, subs: string): bool =
|
|
let le = subs.len
|
|
if le > s.len: return false
|
|
s[^le .. ^1].eqIdent subs
|
|
|
|
proc continuesWithIdent(s, subs: string, start: int): bool =
|
|
s.substr(start, start+subs.high).eqIdent subs
|
|
|
|
proc endsWithIdent(s, subs: string, endIdx: var int): bool =
|
|
endIdx.dec subs.len
|
|
result = s.continuesWithIdent(subs, endIdx+1)
|
|
|
|
proc containsObjectOf(x: string): bool =
|
|
const sep = ' '
|
|
var idx = x.rfind(sep)
|
|
if idx == -1: return
|
|
template eatWord(word) =
|
|
while x[idx] == sep: idx.dec
|
|
result = x.endsWithIdent(word, idx)
|
|
if not result: return
|
|
eatWord "of"
|
|
eatWord "object"
|
|
result = true
|
|
|
|
proc endsWithLineContinuationToken(x: string): bool =
|
|
result = false
|
|
for tok in LineContinuationTokens:
|
|
if x.endsWithIdent(tok):
|
|
return true
|
|
result = x.containsObjectOf
|
|
|
|
proc endsWithOpr*(x: string): bool =
|
|
result = x.endsWith(LineContinuationOprs)
|
|
|
|
proc continueLine(line: string, inTripleString: bool): bool {.inline.} =
|
|
result = inTripleString or line.len > 0 and (
|
|
line[0] == ' ' or
|
|
line.endsWith(LineContinuationOprs+AdditionalLineContinuationOprs) or
|
|
line.endsWithLineContinuationToken()
|
|
)
|
|
|
|
proc countTriples(s: string): int =
|
|
result = 0
|
|
var i = 0
|
|
while i+2 < s.len:
|
|
if s[i] == '"' and s[i+1] == '"' and s[i+2] == '"':
|
|
inc result
|
|
inc i, 2
|
|
inc i
|
|
|
|
proc llReadFromStdin(s: PLLStream, buf: pointer, bufLen: int): int =
|
|
s.s = ""
|
|
s.rd = 0
|
|
var line = newStringOfCap(120)
|
|
var triples = 0
|
|
while true:
|
|
if not readLineFromStdin(if s.s.len == 0: ">>> " else: "... ", line):
|
|
# now readLineFromStdin meets EOF (ctrl-D/Z) or ctrl-C
|
|
quit()
|
|
s.s.add(line)
|
|
s.s.add("\n")
|
|
inc triples, countTriples(line)
|
|
if not continueLine(line, (triples and 1) == 1): break
|
|
inc(s.lineOffset)
|
|
result = min(bufLen, s.s.len - s.rd)
|
|
if result > 0:
|
|
copyMem(buf, addr(s.s[s.rd]), result)
|
|
inc(s.rd, result)
|
|
|
|
proc llStreamRead*(s: PLLStream, buf: pointer, bufLen: int): int =
|
|
case s.kind
|
|
of llsNone:
|
|
result = 0
|
|
of llsString:
|
|
result = min(bufLen, s.s.len - s.rd)
|
|
if result > 0:
|
|
copyMem(buf, addr(s.s[0 + s.rd]), result)
|
|
inc(s.rd, result)
|
|
of llsFile:
|
|
result = readBuffer(s.f, buf, bufLen)
|
|
of llsStdIn:
|
|
if s.onPrompt!=nil: s.onPrompt()
|
|
result = s.repl(s, buf, bufLen)
|
|
|
|
proc llStreamReadLine*(s: PLLStream, line: var string): bool =
|
|
setLen(line, 0)
|
|
case s.kind
|
|
of llsNone:
|
|
result = true
|
|
of llsString:
|
|
while s.rd < s.s.len:
|
|
case s.s[s.rd]
|
|
of '\r':
|
|
inc(s.rd)
|
|
if s.s[s.rd] == '\n': inc(s.rd)
|
|
break
|
|
of '\n':
|
|
inc(s.rd)
|
|
break
|
|
else:
|
|
line.add(s.s[s.rd])
|
|
inc(s.rd)
|
|
result = line.len > 0 or s.rd < s.s.len
|
|
of llsFile:
|
|
result = readLine(s.f, line)
|
|
of llsStdIn:
|
|
result = readLine(stdin, line)
|
|
|
|
proc llStreamWrite*(s: PLLStream, data: string) =
|
|
case s.kind
|
|
of llsNone, llsStdIn:
|
|
discard
|
|
of llsString:
|
|
s.s.add(data)
|
|
inc(s.wr, data.len)
|
|
of llsFile:
|
|
write(s.f, data)
|
|
|
|
proc llStreamWriteln*(s: PLLStream, data: string) =
|
|
llStreamWrite(s, data)
|
|
llStreamWrite(s, "\n")
|
|
|
|
proc llStreamWrite*(s: PLLStream, data: char) =
|
|
var c: char
|
|
case s.kind
|
|
of llsNone, llsStdIn:
|
|
discard
|
|
of llsString:
|
|
s.s.add(data)
|
|
inc(s.wr)
|
|
of llsFile:
|
|
c = data
|
|
discard writeBuffer(s.f, addr(c), sizeof(c))
|
|
|
|
proc llStreamWrite*(s: PLLStream, buf: pointer, buflen: int) =
|
|
case s.kind
|
|
of llsNone, llsStdIn:
|
|
discard
|
|
of llsString:
|
|
if buflen > 0:
|
|
setLen(s.s, s.s.len + buflen)
|
|
copyMem(addr(s.s[0 + s.wr]), buf, buflen)
|
|
inc(s.wr, buflen)
|
|
of llsFile:
|
|
discard writeBuffer(s.f, buf, buflen)
|
|
|
|
proc llStreamReadAll*(s: PLLStream): string =
|
|
const
|
|
bufSize = 2048
|
|
case s.kind
|
|
of llsNone, llsStdIn:
|
|
result = ""
|
|
of llsString:
|
|
if s.rd == 0: result = s.s
|
|
else: result = substr(s.s, s.rd)
|
|
s.rd = s.s.len
|
|
of llsFile:
|
|
result = newString(bufSize)
|
|
var bytes = readBuffer(s.f, addr(result[0]), bufSize)
|
|
var i = bytes
|
|
while bytes == bufSize:
|
|
setLen(result, i + bufSize)
|
|
bytes = readBuffer(s.f, addr(result[i + 0]), bufSize)
|
|
inc(i, bytes)
|
|
setLen(result, i)
|