https://github.com/eddiecorrigall/elang
Toy programming language interpreted with Python
https://github.com/eddiecorrigall/elang
ast interpreter lexer parser python walker
Last synced: 18 days ago
JSON representation
Toy programming language interpreted with Python
- Host: GitHub
- URL: https://github.com/eddiecorrigall/elang
- Owner: eddiecorrigall
- Created: 2021-12-11T06:22:28.000Z (over 4 years ago)
- Default Branch: master
- Last Pushed: 2022-10-17T13:12:56.000Z (over 3 years ago)
- Last Synced: 2025-03-03T16:11:23.704Z (over 1 year ago)
- Topics: ast, interpreter, lexer, parser, python, walker
- Language: Python
- Homepage:
- Size: 11.5 MB
- Stars: 1
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
elang
=====
Toy programming language written by yours truely.
- Purely Python with standard library
- Parser: recursive descent
- Expressions in polish notation
- Abstract Syntax Tree Walker
- Literals: integer, character, string, array, map
- Scope / name binding

## Use CLI
```bash
# Create virtual environment
python3 -m venv venv
# Activate virual environment
source venv/bin/activate
# Install depedencies
pip3 install -r requirements.txt
# Add parent working directory to PATH
export PATH="$(pwd):$PATH"
# List help contents for elang
elang --help
# Run test suit
elang test
# Deactivate virtual environment
deactivate
```
### Example
Produce Abstract Syntax Tree as standard output in JSON format.
```bash
$ echo 'print("Hello world");' | elang lex | elang parse --format json
{
"type": "SEQUENCE",
"left": {
"type": "PRINT_STRING",
"left": {
"type": "STR",
"value": "\"Hello world\""
}
}
}
```
Run program.
```bash
$ echo 'print("Hello world");' | elang run
Hello world
```
## Python API
```python
lex = Lexer()
tokens = Lexer.from_token_file(file)
tokens = lex.from_program_file(file)
tokens = lex.from_program_lines(lines)
tokens = lex(program_line)
line_generator = Lexer.as_lines(tokens)
```
```python
parse = Parser()
ast = parse(tokens)
ast_dict = ast.as_dict()
json_str = ast.as_json()
line_generator = ast.as_lines()
```
```python
walk = Walker()
walk(ast)
```
## Run Tests
```bash
export PYTHONPATH="$(pwd)"
# Run all tests
python3 -m unittest discover --verbose --start-directory tests/
# Run specific test suite
python3 -m unittest --verbose tests/test_lexer_tokens.py
# Run specific test
python3 -m unittest --verbose tests.test_lexer_integer_literal.TestIntegerLiteral.test_positive_integers
```
## References
- https://rosettacode.org/wiki/Compiler/lexical_analyzer
- https://en.wikibooks.org/wiki/Compiler_Construction/Syntax_Analysis
- https://docs.python.org/3/library/re.html#writing-a-tokenizer
- https://en.wikipedia.org/wiki/Operator-precedence_parser
## TODO
- [ ] boolean
- [ ] implement static array
- [x] assign single value
- [x] assign row
- [ ] binary operation (eg multiply) row
- [x] print
- [x] access single value
- [x] access row
- [ ] implement hash table
- [x] default value
- [x] assign single value
- [x] access single value
- [x] print
- [ ] access keys
- [ ] access values
- [ ] loop
- [x] while with condition and statement
- [ ] halt loop
- [ ] continue loop
- [ ] variables
- [x] int
- [x] char
- [x] string
- [x] array
- [x] map
- [ ] constants / immutable value
- [x] typing / immutable type
- [x] variable scope
- [ ] provide token context with AST error
- [x] print statement with multiple arguments
- [x] handle print string escape characters
- [x] assert statement throws error and causes walker to exist with non-zero status code
- test `putc('');` => error
- test `putc('x');` => 'x'
- test `putc(120);` => 'x'
- test `print("");` => ''
- test `print("x");` => 'x'
- test `print('x');` => '120'
- test `print(120);` => '120'
- test `print(["Hello", " ", "world", ]);` => 'Hello world'
- test `print([["Hello", " ", "wolrd",]]);` => 'Hello world'