Blame view

context.nim 4.17 KB
3f64a372   Göran Krampe   First commit
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
  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: