Skip to content

Commit

Permalink
Release 1.2.0 (#17)
Browse files Browse the repository at this point in the history
- Add example to sum the given list
- Version up plare to 1.2.0
  • Loading branch information
henrylee97 authored Jul 11, 2024
1 parent c4cc7d3 commit 9627b90
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 1 deletion.
41 changes: 41 additions & 0 deletions examples/sum_of_list/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Sum of list with Plare

`sum.py` offers a simple sum of list implemented with `plare`.
To test the program first, run the following command.
```bash
$ python sum.py "[1, 2, 3, 4, 5]"
```
The program will parse the input, interprete, and show the answer as follows.
```
15
```

## Defining Language
To make a parser, first thing to do is defining a language.
The following is the language of the sum of list, where `N` represents an integer:
```
list -> N::list
| [N]
| []
```

A node in AST must initialized with `Token` objects or other node objects.
That is, You need to define 3 different type of list, which are initialized with different arguments, to support the arbitrary length of list: `IntList`, `SingleIntList`, and `EmptyIntList`.

## Defining Lexer
A lexer will tokenize the given source string.
To make a lexer you only have to do is make tokens that inherits`Token` class.
In this example, you only need to define 4 tokens: `COMMA`, `LBRACKET`, `RBRACKET`, and `NUM`.

## Defining Parser
A parser will parse the tokens and build an AST.
The CFG for the list is as follows:
```
list -> LBRACKET items RBRACKET -> $1
items -> N COMMA items -> IntList($0, $2)
| N -> SingleIntList($0)
| -> EmptyIntList()
```

## Implementation
You can find implementation of the above example in `sum.py`.
96 changes: 96 additions & 0 deletions examples/sum_of_list/sum.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
from __future__ import annotations

from argparse import ArgumentParser
from typing import Any

from plare.lexer import Lexer
from plare.parser import Parser
from plare.token import Token


class NUM(Token):
value: int

def __init__(self, value: str, *, lineno: int, offset: int) -> None:
super().__init__(value, lineno=lineno, offset=offset)
self.value = int(value)

def __hash__(self) -> int:
return hash(self.value) + super().__hash__()

def __eq__(self, other: Any) -> bool:
return (
isinstance(other, self.__class__)
and self.value == other.value
and super().__eq__(other)
)


class LBRACKET(Token):
pass


class RBRACKET(Token):
pass


class COMMA(Token):
pass


class IntList:
items: list[int]

def __init__(self, item: NUM, tail: IntList) -> None:
self.items = [item.value, *tail.items]


class SingleIntList(IntList):
def __init__(self, item: NUM) -> None:
self.items = [item.value]


class EmptyIntList(IntList):
def __init__(self) -> None:
self.items = []


def main() -> None:
argparser = ArgumentParser()
argparser.add_argument("input", type=str)
args = argparser.parse_args()

lexer = Lexer(
{
"start": [
(r"[ \t\n]+", "start"),
(r"\[", LBRACKET),
(r"\]", RBRACKET),
(r",", COMMA),
(r"-?\d+", NUM),
]
}
)

parser = Parser[IntList](
{
"list": [
([LBRACKET, "items", RBRACKET], None, [1]),
],
"items": [
([NUM, COMMA, "items"], IntList, [0, 2]),
([NUM], SingleIntList, [0]),
([], EmptyIntList, []),
],
}
)

lexbuf = lexer.lex("start", args.input)
parsed = parser.parse("list", lexbuf)
if isinstance(parsed, Token): # Just for type checking
raise Exception("Something went wrong")
print(sum(parsed.items))


if __name__ == "__main__":
main()
2 changes: 1 addition & 1 deletion plare/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "1.1.0"
__version__ = "1.2.0"

0 comments on commit 9627b90

Please sign in to comment.