Blame view

context2.nim 4.15 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
  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: