this repo has no description

Split parser into main entrypoint and two helpers (#211)

`parse` now calls into `parse_binary` and `parse_unary` and is a bit easier to read.

authored by

Abel Sen and committed by
GitHub
2af3efde f8fdca63

+38 -27
+38 -27
scrapscript.py
··· 351 351 352 352 353 353 def parse_assign(tokens: typing.List[Token], p: float = 0) -> "Assign": 354 - assign = parse(tokens, p) 354 + assign = parse_binary(tokens, p) 355 355 if isinstance(assign, Spread): 356 356 return Assign(Var("..."), assign) 357 357 if not isinstance(assign, Assign): ··· 371 371 gensym_reset() 372 372 373 373 374 - def parse(tokens: typing.List[Token], p: float = 0) -> "Object": 374 + def parse_unary(tokens: typing.List[Token], p: float) -> "Object": 375 375 if not tokens: 376 376 raise UnexpectedEOFError("unexpected end of input") 377 377 token = tokens.pop(0) 378 378 l: Object 379 379 if isinstance(token, IntLit): 380 - l = Int(token.value) 380 + return Int(token.value) 381 381 elif isinstance(token, FloatLit): 382 - l = Float(token.value) 382 + return Float(token.value) 383 383 elif isinstance(token, Name): 384 384 # TODO: Handle kebab case vars 385 - l = Var(token.value) 385 + return Var(token.value) 386 386 elif isinstance(token, VariantToken): 387 387 # It needs to be higher than the precedence of the -> operator so that 388 388 # we can match variants in MatchFunction ··· 390 390 # we can use #true() and #false() in boolean expressions 391 391 # It needs to be higher than the precedence of juxtaposition so that 392 392 # f #true() #false() is parsed as f(TRUE)(FALSE) 393 - l = Variant(token.value, parse(tokens, PS[""].pr + 1)) 393 + return Variant(token.value, parse_binary(tokens, PS[""].pr + 1)) 394 394 elif isinstance(token, BytesLit): 395 395 base = token.base 396 396 if base == 85: ··· 403 403 l = Bytes(base64.b16decode(token.value)) 404 404 else: 405 405 raise ParseError(f"unexpected base {base!r} in {token!r}") 406 + return l 406 407 elif isinstance(token, StringLit): 407 - l = String(token.value) 408 + return String(token.value) 408 409 elif token == Operator("..."): 409 410 if tokens and isinstance(tokens[0], Name): 410 411 name = tokens[0].value 411 412 tokens.pop(0) 412 - l = Spread(name) 413 + return Spread(name) 413 414 else: 414 - l = Spread() 415 + return Spread() 415 416 elif token == Operator("|"): 416 - expr = parse(tokens, PS["|"].pr) # TODO: make this work for larger arities 417 + expr = parse_binary(tokens, PS["|"].pr) # TODO: make this work for larger arities 417 418 if not isinstance(expr, Function): 418 419 raise ParseError(f"expected function in match expression {expr!r}") 419 420 cases = [MatchCase(expr.arg, expr.body)] 420 421 while tokens and tokens[0] == Operator("|"): 421 422 tokens.pop(0) 422 - expr = parse(tokens, PS["|"].pr) # TODO: make this work for larger arities 423 + expr = parse_binary(tokens, PS["|"].pr) # TODO: make this work for larger arities 423 424 if not isinstance(expr, Function): 424 425 raise ParseError(f"expected function in match expression {expr!r}") 425 426 cases.append(MatchCase(expr.arg, expr.body)) 426 - l = MatchFunction(cases) 427 + return MatchFunction(cases) 427 428 elif isinstance(token, LeftParen): 428 429 if isinstance(tokens[0], RightParen): 429 430 l = Hole() 430 431 else: 431 432 l = parse(tokens) 432 433 tokens.pop(0) 434 + return l 433 435 elif isinstance(token, LeftBracket): 434 436 l = List([]) 435 437 token = tokens[0] 436 438 if isinstance(token, RightBracket): 437 439 tokens.pop(0) 438 440 else: 439 - l.items.append(parse(tokens, 2)) 441 + l.items.append(parse_binary(tokens, 2)) 440 442 while not isinstance(tokens.pop(0), RightBracket): 441 443 if isinstance(l.items[-1], Spread): 442 444 raise ParseError("spread must come at end of list match") 443 445 # TODO: Implement .. operator 444 - l.items.append(parse(tokens, 2)) 446 + l.items.append(parse_binary(tokens, 2)) 447 + return l 445 448 elif isinstance(token, LeftBrace): 446 449 l = Record({}) 447 450 token = tokens[0] ··· 456 459 # TODO: Implement .. operator 457 460 assign = parse_assign(tokens, 2) 458 461 l.data[assign.name.name] = assign.value 462 + return l 459 463 elif token == Operator("-"): 460 464 # Unary minus 461 465 # Precedence was chosen to be higher than binary ops so that -a op 462 466 # b is (-a) op b and not -(a op b). 463 467 # Precedence was chosen to be higher than function application so that 464 468 # -a b is (-a) b and not -(a b). 465 - r = parse(tokens, HIGHEST_PREC + 1) 466 - l = Binop(BinopKind.SUB, Int(0), r) 469 + r = parse_binary(tokens, HIGHEST_PREC + 1) 470 + return Binop(BinopKind.SUB, Int(0), r) 467 471 else: 468 472 raise ParseError(f"unexpected token {token!r}") 469 473 474 + 475 + def parse_binary(tokens: typing.List[Token], p: float) -> "Object": 476 + l: Object = parse_unary(tokens, p) 470 477 while True: 471 478 if not tokens: 472 479 break ··· 478 485 pl, pr = prec.pl, prec.pr 479 486 if pl < p: 480 487 break 481 - l = Apply(l, parse(tokens, pr)) 488 + l = Apply(l, parse_binary(tokens, pr)) 482 489 continue 483 490 prec = PS[op.value] 484 491 pl, pr = prec.pl, prec.pr ··· 488 495 if op == Operator("="): 489 496 if not isinstance(l, Var): 490 497 raise ParseError(f"expected variable in assignment {l!r}") 491 - l = Assign(l, parse(tokens, pr)) 498 + l = Assign(l, parse_binary(tokens, pr)) 492 499 elif op == Operator("->"): 493 - l = Function(l, parse(tokens, pr)) 500 + l = Function(l, parse_binary(tokens, pr)) 494 501 elif op == Operator("|>"): 495 - l = Apply(parse(tokens, pr), l) 502 + l = Apply(parse_binary(tokens, pr), l) 496 503 elif op == Operator("<|"): 497 - l = Apply(l, parse(tokens, pr)) 504 + l = Apply(l, parse_binary(tokens, pr)) 498 505 elif op == Operator(">>"): 499 - r = parse(tokens, pr) 506 + r = parse_binary(tokens, pr) 500 507 varname = gensym() 501 508 l = Function(Var(varname), Apply(r, Apply(l, Var(varname)))) 502 509 elif op == Operator("<<"): 503 - r = parse(tokens, pr) 510 + r = parse_binary(tokens, pr) 504 511 varname = gensym() 505 512 l = Function(Var(varname), Apply(l, Apply(r, Var(varname)))) 506 513 elif op == Operator("."): 507 - l = Where(l, parse(tokens, pr)) 514 + l = Where(l, parse_binary(tokens, pr)) 508 515 elif op == Operator("?"): 509 - l = Assert(l, parse(tokens, pr)) 516 + l = Assert(l, parse_binary(tokens, pr)) 510 517 elif op == Operator("@"): 511 518 # TODO: revisit whether to use @ or . for field access 512 - l = Access(l, parse(tokens, pr)) 519 + l = Access(l, parse_binary(tokens, pr)) 513 520 else: 514 521 assert isinstance(op, Operator) 515 - l = Binop(BinopKind.from_str(op.value), l, parse(tokens, pr)) 522 + l = Binop(BinopKind.from_str(op.value), l, parse_binary(tokens, pr)) 516 523 return l 524 + 525 + 526 + def parse(tokens: typing.List[Token]) -> "Object": 527 + return parse_binary(tokens, 0) 517 528 518 529 519 530 @dataclass(eq=True, frozen=True, unsafe_hash=True)