context.nim
4.17 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
162
163
164
165
166
167
168
169
170
171
172
173
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
# Context
type
Context* = ref object of RootObj
buffer*: string
position*: int
proc newContext*(buffer: string, position: int): Context =
Context(buffer: buffer, position: position)
# Result
type
Result* = ref object of Context
# Success
type
Success*[T] = ref object of Result
result*: T
proc newSuccess*[T](buffer: string, position: int, value: T): Success =
Success(buffer: buffer, position: position, result: value)
# Failure
type
Failure* = ref object of Result
message*: string
proc newFailure*(buffer: string, position: int, message: string): Failure =
Failure(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(self.buffer, position, value)
method success*[T](self: Context, value: T): Success =
self.success(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: Result): bool =
false
method isFailure*(self: Result): 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*[T](self: Result): T =
discard result
# Dirty trick to make compiler realize I return a T
# T(self)
method get*[T](self: Failure): T =
raise newParseError(self)
method get*[T](self: Success): T =
self.result
method getMessage*(self: Failure): string =
self.message
method isSuccess*(self: Success): bool =
true
method name*(self: Result): string =
"Result"
method name*(self: Success): string =
"Success"
method name*(self: Failure): string =
"Failure"
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: