121 lines
2.7 KiB
Nim
121 lines
2.7 KiB
Nim
|
import
|
||
|
strutils, strformat, parseutils
|
||
|
|
||
|
type
|
||
|
TokenKind* = enum
|
||
|
tIdent = "ident"
|
||
|
tNumber = "number"
|
||
|
tDot = "dot"
|
||
|
tOpenBracket = "["
|
||
|
tCloseBracket = "]"
|
||
|
tEof = "end of file"
|
||
|
tError = "error"
|
||
|
|
||
|
Token* = object
|
||
|
case kind*: TokenKind
|
||
|
of tIdent:
|
||
|
name*: string
|
||
|
of tNumber:
|
||
|
val*: uint64
|
||
|
of tError:
|
||
|
errMsg: string
|
||
|
else:
|
||
|
discard
|
||
|
|
||
|
Lexer* = object
|
||
|
tok*: Token
|
||
|
input: string
|
||
|
pos: int
|
||
|
|
||
|
Parser* = object
|
||
|
lexer: Lexer
|
||
|
|
||
|
NodeKind* = enum
|
||
|
Ident
|
||
|
Number
|
||
|
Dot
|
||
|
ArrayAccess
|
||
|
Error
|
||
|
|
||
|
Node* = ref object {.acyclic.}
|
||
|
case kind*: NodeKind
|
||
|
of Dot:
|
||
|
objVal*, field*: Node
|
||
|
of ArrayAccess:
|
||
|
arrayVal*, index*: Node
|
||
|
of Ident:
|
||
|
name*: string
|
||
|
of Number:
|
||
|
numVal*: uint64
|
||
|
of Error:
|
||
|
errMsg*: string
|
||
|
|
||
|
func advance(lexer: var Lexer) =
|
||
|
if lexer.pos >= lexer.input.len:
|
||
|
lexer.tok = Token(kind: tEof)
|
||
|
else:
|
||
|
let nextChar = lexer.input[lexer.pos]
|
||
|
case nextChar
|
||
|
of IdentStartChars:
|
||
|
lexer.tok = Token(kind: tIdent)
|
||
|
lexer.pos = parseIdent(lexer.input, lexer.tok.name, lexer.pos)
|
||
|
of Whitespace:
|
||
|
lexer.pos = skipWhitespace(lexer.input, lexer.pos)
|
||
|
advance lexer
|
||
|
of Digits:
|
||
|
lexer.tok = Token(kind: tNumber)
|
||
|
lexer.pos = parseBiggestUInt(lexer.input, lexer.tok.val, lexer.pos)
|
||
|
of '[':
|
||
|
lexer.tok = Token(kind: tOpenBracket)
|
||
|
inc lexer.pos
|
||
|
of ']':
|
||
|
lexer.tok = Token(kind: tCloseBracket)
|
||
|
inc lexer.pos
|
||
|
of '.':
|
||
|
lexer.tok = Token(kind: tDot)
|
||
|
inc lexer.pos
|
||
|
else:
|
||
|
lexer.tok = Token(
|
||
|
kind: tError,
|
||
|
errMsg: &"Unexpected character '{nextChar}' at position {lexer.pos}")
|
||
|
|
||
|
func init*(T: type Lexer, src: string): Lexer =
|
||
|
result.input = src
|
||
|
result.pos = 0
|
||
|
advance result
|
||
|
|
||
|
func init*(T: type Parser, src: string): Parser =
|
||
|
Parser(lexer: Lexer.init(src))
|
||
|
|
||
|
func expr(parser: var Parser): Node =
|
||
|
template unexpectedToken =
|
||
|
return Node(kind: Error, errMsg: &"Unexpected {parser.lexer.tok.kind} token")
|
||
|
|
||
|
case parser.lexer.tok.kind
|
||
|
of tIdent:
|
||
|
result = Node(kind: Ident, name: parser.lexer.tok.name)
|
||
|
of tNumber:
|
||
|
return Node(kind: Number, numVal: parser.lexer.tok.val)
|
||
|
else:
|
||
|
unexpectedToken
|
||
|
|
||
|
advance parser.lexer
|
||
|
case parser.lexer.tok.kind
|
||
|
of tOpenBracket:
|
||
|
advance parser.lexer
|
||
|
result = Node(kind: ArrayAccess, arrayVal: result, index: parser.expr)
|
||
|
if parser.lexer.tok.kind != tCloseBracket:
|
||
|
unexpectedToken
|
||
|
else:
|
||
|
advance parser.lexer
|
||
|
of tDot:
|
||
|
advance parser.lexer
|
||
|
return Node(kind: Dot, objVal: result, field: parser.expr)
|
||
|
else:
|
||
|
discard
|
||
|
|
||
|
func parse*(input: string): Node =
|
||
|
var p = Parser.init(input)
|
||
|
p.expr
|
||
|
|