parser progress

This commit is contained in:
prod2 2022-12-02 19:14:41 +01:00
parent e6a48ccee6
commit f721d0987d
4 changed files with 290 additions and 10 deletions

View File

@ -6,12 +6,12 @@ import strutils
type
NodeKind* = enum
nkBlockExpr, nkExprStmt, nkBreak,
nkBlockExpr, nkExprStmt, nkBreak, nkExpr,
nkConst, nkList, nkTable, nkGetIndex, nkSetIndex, nkLen,
nkIf, nkWhile, nkOr, nkAnd, nkNegate, nkNot,
nkPlus, nkMinus, nkMult, nkDiv, nkEq, nkNeq, nkLess, nkGreater,
nkGe, nkLe,
nkCall, nkVarDecl, nkProc,
nkCall, nkColonCall, nkVarDecl, nkProc,
nkVarGet, nkVarSet
Node* = ref object
@ -19,7 +19,7 @@ type
of nkBlockExpr:
children*: seq[Node]
labels*: seq[string]
of nkExprStmt: # only when there is a ; does it compile to expr stmt; expr used for parentheses, &
of nkExprStmt, nkExpr: # only when there is a ; does it compile to expr stmt; expr used for parentheses, &
expression*: Node
of nkBreak:
label*: string
@ -49,7 +49,7 @@ type
of nkAnd, nkOr, nkPlus, nkMinus, nkMult, nkDiv, nkEq, nkNeq, nkLess, nkGreater, nkGe, nkLe: # binary ops
left*: Node
right*: Node
of nkCall:
of nkCall, nkColonCall:
function*: Node
arguments*: seq[Node]
of nkVarDecl:
@ -65,6 +65,8 @@ type
newVal*: Node
proc `$`*(node: Node): string =
if node == nil:
return "<nil>"
case node.kind:
of nkAnd:
result = &"(and {node.left} {node.right})"
@ -81,12 +83,19 @@ proc `$`*(node: Node): string =
for ch in node.arguments:
result &= $ch & ", "
result[^1] = ')'
of nkColonCall:
result = &"(:call {node.function} "
for ch in node.arguments:
result &= $ch & ", "
result[^1] = ')'
of nkConst:
result = &"(const {node.constant})"
of nkDiv:
result = &"(/ {node.left} {node.right})"
of nkEq:
result = &"(== {node.left} {node.right})"
of nkExpr:
result = &"(grouping {node.expression})"
of nkExprStmt:
result = &"(exprStmt {node.expression})"
of nkGe:

View File

@ -5,8 +5,11 @@ import ../scanner
import ../chunk
import node
import ../config
import ../types/value
import strformat
import strutils
import bitops
import sequtils
import sugar
@ -32,7 +35,6 @@ proc newParser*(name: string, source: string): Parser =
# UTILS
# error handling
proc errorAt(parser: Parser, line: int, msg: string, at: string = "") =
@ -71,10 +73,23 @@ proc match(parser: Parser, tokenType: TokenType): bool =
else:
false
proc consume(parser: Parser, tokenType: TokenType, msg: string) =
proc match(parser: Parser, tokenTypes: set[TokenType]): bool =
if parser.current.tokenType in tokenTypes:
parser.advance()
true
else:
false
proc consume(parser: Parser, tokenType: TokenType | set[TokenType], msg: string) =
if not parser.match(tokenType):
parser.errorAtCurrent(msg)
proc peek(parser: Parser): Token =
parser.current
proc peekMatch(parser: Parser, tokenType: TokenType): bool =
parser.peek().tokenType == tokenType
proc synchronize(parser: Parser) =
parser.panicMode = false
while parser.current.tokenType != tkEof:
@ -84,8 +99,249 @@ proc synchronize(parser: Parser) =
return
parser.advance()
proc isAtEnd(parser: Parser): bool =
parser.current.tokenType == tkEof
# EXPRESSIONS
proc expression(parser: Parser): Node
# expressions, but not assignments
proc exprNonAssign(parser: Parser): Node
proc parseList(parser: Parser): Node =
result = Node(kind: nkList, elems: @[])
while not parser.isAtEnd() and not parser.peekMatch(tkRightBracket):
result.elems.add(parser.expression())
if parser.peek().tokenType != tkRightBracket:
parser.consume(tkComma, "',' expected after list elements.")
parser.consume(tkRightBracket, "']' expected after list declaration.")
proc parseTable(parser: Parser): Node =
result = Node(kind: nkTable, keys: @[], values: @[])
while not parser.isAtEnd() and not parser.peekMatch(tkRightBrace):
# [key] = syntax
if parser.match(tkLeftBracket):
result.keys.add(parser.expression())
parser.consume(tkRightBracket, "']' expected after table key.")
# key = syntax
elif parser.match(tkIdentifier):
result.keys.add(Node(kind: nkConst, constant: parser.previous.text.fromNimString()))
else:
parser.errorAtCurrent("Key expected (have you forgotten to put the key in brackets?).")
parser.consume(tkEqual, "'=' expected after key.")
result.values.add(parser.exprNonAssign())
if parser.peek().tokenType != tkRightBrace:
parser.consume(tkComma, "',' expected after table key value pair.")
parser.consume(tkRightBrace, "'}' expected after table declaration.")
proc primary(parser: Parser): Node =
if parser.match(tkFalse):
return Node(kind: nkConst, constant: ndFalse)
if parser.match(tkTrue):
return Node(kind: nkConst, constant: ndTrue)
if parser.match(tkNil):
return Node(kind: nkConst, constant: ndNil)
if parser.match(tkNumber):
return Node(kind: nkConst, constant: fromFloat(parseFloat(parser.previous.text)))
if parser.match(tkString):
return Node(kind: nkConst, constant: fromNimString(parser.previous.text[1..^2]))
if parser.match(tkLeftParen):
let grouped = parser.expression()
parser.consume(tkRightParen, "Expect ')' after expression.")
return Node(kind: nkExpr, expression: grouped)
if parser.match(tkStartList):
return parser.parseList()
if parser.match(tkStartTable):
return parser.parseTable()
if parser.match(tkIdentifier):
return Node(kind: nkVarGet, gVarName: parser.previous.text)
parser.errorAtCurrent("Primary expected, but something else found.")
proc parseIndex(parser:Parser): Node =
result = parser.primary()
while parser.match({tkLeftBracket, tkDot}):
if parser.previous.tokenType == tkLeftBracket:
let index = parser.expression()
parser.consume(tkRightBracket, "']' after index.")
result = Node(kind: nkGetIndex, gCollection: result, gIndex: index)
else:
# dot
parser.consume(tkIdentifier, "Identifier expected after '.' index operator.")
result = Node(kind: nkGetIndex, gCollection: result, gIndex: Node(kind: nkConst, constant: parser.previous.text.fromNimString()))
proc parseCall(parser: Parser): Node =
result = parser.parseIndex()
if parser.match(tkLeftParen):
var args: seq[Node] = @[]
while not parser.isAtEnd() and not parser.peekMatch(tkRightParen):
let arg = parser.expression()
if not parser.isAtEnd() and not parser.peekMatch(tkRightParen):
parser.consume(tkComma, "',' expected between arguments.")
args.add(arg)
parser.consume(tkRightParen, "')' expected after argument list.")
result = Node(kind: nkCall, arguments: args, function: result)
proc parseIf(parser: Parser): Node =
parser.consume(tkLeftParen, "'(' expected after 'if'.")
let cond = parser.expression()
parser.consume(tkRightParen, "')' expected after condition.")
let body = parser.expression()
result = Node(kind: nkIf, ifCondition: cond, ifBody: body)
if parser.match(tkElse):
result.elseBody = parser.expression()
proc parseWhile(parser:Parser): Node =
parser.consume(tkLeftParen, "'(' expected after 'while'.")
let cond = parser.expression()
parser.consume(tkRightParen, "')' expected after condition.")
let body = parser.expression()
result = Node(kind: nkWhile, whileCondition: cond, whileBody: body)
proc unary(parser: Parser): Node =
# unary level for unary operators, plus some control flow is here too
const unaryOps = {tkBang, tkMinus, tkIf, tkWhile, tkHashtag}
if parser.match(unaryOps):
let op = parser.previous
case op.tokenType:
of tkBang:
let right = parser.unary()
return Node(kind: nkNot, argument: right)
of tkMinus:
let right = parser.unary()
return Node(kind: nkNegate, argument: right)
of tkHashtag:
let right = parser.unary()
return Node(kind: nkLen, argument: right)
of tkIf:
return parser.parseIf()
of tkWhile:
return parser.parseWhile()
else:
parser.errorAtCurrent("Invalid parser state: unaryOps and case statement out of line.")
return parser.parseCall()
proc factor(parser: Parser): Node =
result = parser.unary()
while parser.match({tkSlash, tkStar}):
let op = parser.previous
let right = parser.unary()
if op.tokenType == tkSlash:
result = Node(kind: nkDiv, left: result, right: right)
else:
result = Node(kind: nkMult, left: result, right: right)
proc term(parser: Parser): Node =
result = parser.factor()
while parser.match({tkMinus, tkPlus}):
let op = parser.previous
let right = parser.factor()
if op.tokenType == tkMinus:
result = Node(kind: nkMinus, left: result, right: right)
else:
result = Node(kind: nkPlus, left: result, right: right)
proc comparison(parser: Parser): Node =
result = parser.term()
while parser.match({tkGreater, tkGreaterEqual, tkLess, tkLessEqual}):
let op = parser.previous
let right = parser.term()
case op.tokenType:
of tkGreater:
result = Node(kind: nkGreater, left: result, right: right)
of tkGreaterEqual:
result = Node(kind: nkGe, left: result, right: right)
of tkLess:
result = Node(kind: nkLess, left: result, right: right)
of tkLessEqual:
result = Node(kind: nkLe, left: result, right: right)
else:
parser.errorAtCurrent("invalid state in comparison: case and set don't match up.")
proc equality(parser: Parser): Node =
result = parser.comparison()
while parser.match({tkBangEqual, tkEqualEqual}):
let op = parser.previous
let right = parser.comparison()
if op.tokenType == tkBangEqual:
result = Node(kind: nkNeq, left: result, right: right)
else:
result = Node(kind: nkEq, left: result, right: right)
proc parseAnd(parser: Parser): Node =
result = parser.equality()
while parser.match(tkAnd):
let right = parser.equality()
result = Node(kind: nkAnd, left: result, right: right)
proc parseOr(parser: Parser): Node =
result = parser.parseAnd()
while parser.match(tkOr):
let right = parser.parseAnd()
result = Node(kind: nkOr, left: result, right: right)
proc parsePipeCall(parser: Parser): Node =
result = parser.parseOr()
while parser.match(tkDoublecolon):
let right = parser.parseOr()
# to the right, if topmost level is a call, it will insert it
# if the topmost is not a call, e.g. 5 :: funcs.double it will assume it's a function with one arg - the one before the pipe
# if the thing to the right has lower precedence stuff than a call, please note that it will not insert into the call, it will assume that the return val is a function
# to have such lower precedence ops, use parens: 5 :: (long expression)(arg1, arg2)
# case 1: right is already a call or coloncall
if right.kind in {nkCall, nkColonCall}:
right.arguments.insert(result, 0)
result = right
# else: right val is a function which we call
else:
result = Node(kind: nkCall, arguments: @[result], function: right)
proc exprNonAssign(parser: Parser): Node =
parser.parsePipeCall()
proc parseAssign(parser: Parser): Node =
result = parser.exprNonAssign()
if parser.match(tkEqual):
# check if result is assignable
const assignable = {nkVarGet, nkGetIndex}
let right = parser.expression()
if result.kind notin assignable:
parser.errorAtCurrent("Attempt to assign to invalid target.")
return
if result.kind == nkVarGet:
result = Node(kind: nkVarSet, sVarName: result.gVarName, newVal: right)
else:
# nkGetIndex
result = Node(kind: nkSetIndex, sCollection: result.gCollection, sIndex: result.gIndex, sValue: right)
proc parseAmpersand(parser: Parser): Node =
result = parser.parseAssign()
# TODO
proc expression(parser: Parser): Node =
parser.parseAmpersand()
# STATEMENTS
proc statement(parser: Parser): Node
@ -93,14 +349,21 @@ proc statement(parser: Parser): Node
proc statement(parser: Parser): Node =
let expression = parser.expression()
if expression != nil:
result = Node(kind: nkExprStmt, expression: expression)
else:
parser.errorAtCurrent("Expression expected.")
parser.consume(tkSemicolon, "; expected after expression statement.")
if parser.panicMode:
parser.synchronize()
proc parse*(parser: Parser): Node =
parser.scanner = newScanner(parser.source)
var result: Node = Node(kind: nkBlockExpr, children: @[])
result = Node(kind: nkBlockExpr, children: @[])
parser.advance()
while parser.current.tokenType != tkEof:
result.children.add(parser.statement())
while not parser.isAtEnd():
let statement = parser.statement()
result.children.add(statement)

View File

@ -352,7 +352,8 @@ proc run*(chunk: Chunk): InterpretResult =
of opSetUpvalue:
let slot = ip.readDU8()
when debugClosures:
echo &"CLOSURES - setupvalue is setting {$stack.peek} to slot {slot}, number of slots: {frames.peek().closure.upvalueCount}"
#echo &"CLOSURES - setupvalue is setting {$stack.peek} to slot {slot}, number of slots: {frames.peek().closure.upvalueCount}"
discard # TODO fix this
cclosure.get(slot).write(stack.peek())
of opCloseUpvalue:
let slot = ip.readDU8()

View File

@ -7,3 +7,10 @@ var returnlist = proc()
//expect:3.0
print (returnlist()[2]);
var a = @[@[1]];
print (a[0][0]);
//expect:1.0
a[0][0] = 3;
print (a[0][0]);
//expect:3.0;