context2.nim
4.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
import hashes
# Converts the position index in a buffer to a line and column tuple.
# This code is an adaptation of splitLines.
proc findLineAndColumn(s: string, position: int): tuple[line, col: int] =
var first = 0
var last = 0
var line = 0
while true:
while s[last] notin {'\0', '\c', '\l'}: inc(last)
# First line found
inc(line)
if last > position:
let col = position - first
return (line, col)
# skip newlines:
if s[last] == '\l': inc(last)
elif s[last] == '\c':
inc(last)
if s[last] == '\l': inc(last)
else: break # was '\0'
first = last
type
ContextKind = enum ckContext, ckSuccess, ckFailure
# Context
type
Context* = object
buffer*: string
position*: int
case kind: ContextKind
of ckContext:
nil
of ckSuccess:
value*: RootRef
of ckFailure:
message*: string
type Success = distinct Context
type Failure = distinct Context
proc newContext*(buffer: string, position: int): Context =
Context(kind: ckContext, buffer: buffer, position: position)
proc newSuccess*(buffer: string, position: int, value: RootRef): Success =
Success(Context(kind: ckSuccess, buffer: buffer, position: position, value: value))
proc newFailure*(buffer: string, position: int, message: string): Failure =
Failure(Context(kind: ckFailure, buffer: buffer, position: position, message: message))
# ParseError Exception
type
ParseError* = object of Exception
failure*: Failure
proc newParseError*(failure: Failure): ref ParseError =
new(result)
result.failure = failure
result.msg = failure.message
# Token
type
Token* = ref object of RootObj
buffer*: string
start*: int
stop*: int
value*: RootRef
proc newToken*(buffer: string, start, stop: int, value: RootRef): Token =
Token(buffer: buffer, start: start, stop: stop, value: value)
method getInput*(self: Token): string =
self.buffer.substr(self.start, self.stop)
method getLength*(self: Token): int =
self.stop - self.stop
method getLine*(self: Token): int =
self.buffer.findLineAndColumn(self.start)[0]
method getColumn*(self: Token): int =
self.buffer.findLineAndColumn(self.start)[1]
method `$`*(self: Token): string =
let (line, col) = findLineAndColumn(self.buffer, self.start);
"Token[" & $line & ":" & $col & "]: " & repr(self.value) # $self.value
method hash*(self: Token): THash =
result = self.buffer.hash !& self.start.hash !& self.stop.hash #!& self.value.hash
result = !$result
# We do not need to redefine ==, the generic one works fine.
# But let's see if we can
#method `==`(self, other: Token): bool =
# if (addr(self) == addr(other)):
# return true
# if other.isNil or self.isNil:
# return false
# self[] == other[]
method success[T]*(self: Context, value: T, position: int): Success =
newSuccess[T](self.buffer, position, value)
method success[T]*(self: Context, value: T): Success =
self.success[T](value, self.position)
method failure*(self: Context, message: string, position: int): Failure =
newFailure(self.buffer, position, message)
method failure*(self: Context, message: string): Failure =
self.failure(message, self.position)
method isSuccess*(self: Context): bool =
false
method isFailure*(self: Context): bool =
false
# An abstract method, hmmm, which you can't really do
# in nim. Normally I would like to throw exception here,
# but we need to return a T for compiler to be happy...
method get*(self: Result): =
discard result
# Dirty trick to make compiler realize I return a T
# T(self)
method get*(self: Failure): RootRef =
raise newParseError(self)
method get*(self: Success): RootRef =
self.result
method getMessage*(self: Failure): string =
self.message
method isSuccess*(self: Success): bool =
true
method `$`*(self: Result): string =
let (line, col) = findLineAndColumn(self.buffer, self.position)
self.name & "[" & $line & ":" & $col & "]"
method `$`*(self: Success): string =
procCall($(Result(self))) & ": " & repr(self.result)
method isFailure*(self: Failure): bool =
true
method `$`*(self: Failure): string =
procCall($(Result(self))) & ": " & self.message
# The simplest form of unit tests
#when isMainModule: