├── Cargo.toml
├── assets
└── new_logo.png
├── crates
├── ast
│ ├── src
│ │ ├── lib.rs
│ │ ├── op.rs
│ │ ├── stmt.rs
│ │ ├── expr.rs
│ │ └── ty.rs
│ └── Cargo.toml
├── meta
│ ├── Cargo.toml
│ └── src
│ │ ├── lib.rs
│ │ └── span.rs
├── lexer
│ ├── Cargo.toml
│ └── src
│ │ ├── lib.rs
│ │ └── token_kind.rs
├── compiler
│ ├── Cargo.toml
│ └── src
│ │ └── lib.rs
├── interpreter
│ ├── Cargo.toml
│ └── src
│ │ ├── value
│ │ ├── instance.rs
│ │ ├── list.rs
│ │ ├── class.rs
│ │ ├── function.rs
│ │ ├── builtin.rs
│ │ ├── val.rs
│ │ └── mod.rs
│ │ ├── error.rs
│ │ ├── lib.rs
│ │ ├── env.rs
│ │ └── expr.rs
├── nakala
│ ├── Cargo.toml
│ └── src
│ │ └── main.rs
├── parser
│ ├── Cargo.toml
│ └── src
│ │ ├── source.rs
│ │ ├── symtab.rs
│ │ ├── error.rs
│ │ ├── lib.rs
│ │ └── parser.rs
└── wasm
│ ├── Cargo.toml
│ ├── README.md
│ └── src
│ └── lib.rs
├── examples
├── until.nak
├── fib.nak
├── json_parser.nak
├── closure.nak
├── enums.nak
├── class_this.nak
├── lists.nak
├── linkedlist.nak
├── typechecking.nak
├── ifs.nak
├── functions.nak
├── class.nak
├── stack.nak
└── rule110.nak
├── .gitignore
├── .github
└── workflows
│ └── CI.yml
├── LICENSE
└── README.md
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [workspace]
2 | members = ["crates/*"]
3 |
--------------------------------------------------------------------------------
/assets/new_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nakala-lang/nakala/HEAD/assets/new_logo.png
--------------------------------------------------------------------------------
/crates/ast/src/lib.rs:
--------------------------------------------------------------------------------
1 | pub mod expr;
2 | pub mod op;
3 | pub mod stmt;
4 | pub mod ty;
5 |
--------------------------------------------------------------------------------
/examples/until.nak:
--------------------------------------------------------------------------------
1 | let x = 0;
2 |
3 | until x > 10000 {
4 | print(x);
5 | x = x + 1;
6 | }
7 |
8 | print("Finished - x is now " + x);
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | Cargo.lock
3 | .DS_Store
4 | *.mov
5 | ~/.config/nakala/history.txt
6 | out.cpp
7 | program
8 | crates/wasm/site/node_modules/
9 |
--------------------------------------------------------------------------------
/examples/fib.nak:
--------------------------------------------------------------------------------
1 | func fib(num: int) -> int {
2 | if num <= 1 {
3 | ret num;
4 | }
5 |
6 | ret fib(num - 1) + fib(num - 2);
7 | }
8 |
9 | print(fib(19));
10 |
--------------------------------------------------------------------------------
/examples/json_parser.nak:
--------------------------------------------------------------------------------
1 | let input = "{ name: 'Reagan', age: 22 }";
2 | print(input);
3 |
4 | enum JsonType {
5 | Object,
6 | Number,
7 | String
8 | }
9 |
10 |
11 |
--------------------------------------------------------------------------------
/examples/closure.nak:
--------------------------------------------------------------------------------
1 | func makeCounter() {
2 | let i = 0;
3 |
4 | func count() {
5 | i = i + 1;
6 | print i;
7 | }
8 |
9 | ret count;
10 | }
11 |
12 | let counter = makeCounter();
13 | counter();
14 | counter();
15 |
--------------------------------------------------------------------------------
/examples/enums.nak:
--------------------------------------------------------------------------------
1 | enum ValueType { String, Number, Object }
2 |
3 | if ValueType.String != 0 {
4 | print("Case 1 failed");
5 | }
6 |
7 | let t = ValueType;
8 | if t.String != ValueType.String {
9 | print("Case 2 failed");
10 | }
11 |
--------------------------------------------------------------------------------
/crates/meta/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "meta"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7 |
8 | [dependencies]
9 | miette = "4.7.0"
10 |
--------------------------------------------------------------------------------
/crates/lexer/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "lexer"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7 |
8 | [dependencies]
9 | logos = "0.12.0"
10 | meta = { path = "../meta" }
11 |
--------------------------------------------------------------------------------
/crates/ast/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "ast"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7 |
8 | [dependencies]
9 | lexer = { path = "../lexer" }
10 | meta = { path = "../meta" }
11 |
--------------------------------------------------------------------------------
/crates/meta/src/lib.rs:
--------------------------------------------------------------------------------
1 | mod span;
2 | pub use span::{Span, Spanned};
3 |
4 | pub type SourceId = usize;
5 |
6 | #[macro_export]
7 | macro_rules! trace {
8 | ($x: expr) => {
9 | #[cfg(feature = "trace")]
10 | {
11 | println!("{}", $x)
12 | }
13 | };
14 | }
15 |
--------------------------------------------------------------------------------
/examples/class_this.nak:
--------------------------------------------------------------------------------
1 | class Person {
2 | constructor(first: string, last: string) {
3 | this.first = first;
4 | this.last = last;
5 | }
6 |
7 | greet() {
8 | print "Hi, my name is " + this.first + " " + this.last;
9 | }
10 | }
11 |
12 | let me = Person("Reagan", "McFarland");
13 | me.greet();
14 |
--------------------------------------------------------------------------------
/crates/compiler/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "compiler"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7 |
8 | [dependencies]
9 | parser = { path = "../parser" }
10 | ast = { path = "../ast" }
11 | meta = { path = "../meta" }
12 | miette = { version = "4.7.0" }
13 | thiserror = "1.0.31"
14 |
15 |
--------------------------------------------------------------------------------
/examples/lists.nak:
--------------------------------------------------------------------------------
1 | class Mapper {
2 | constructor(f: (int) -> any) {
3 | this.f = f;
4 | }
5 |
6 | map(vals: [int]) -> [any] {
7 | let i = 0;
8 | until i == len(vals) {
9 | vals[i] = this.f(vals[i]);
10 | i = i + 1;
11 | }
12 |
13 | ret vals;
14 | }
15 | }
16 |
17 | let arr = [1,2,3,4,5];
18 | func add5(x: int) -> int {
19 | ret x + 5;
20 | }
21 | print(Mapper(add5).map(arr));
22 |
--------------------------------------------------------------------------------
/examples/linkedlist.nak:
--------------------------------------------------------------------------------
1 | class Node {
2 | constructor(data: int, next) {
3 | this.data = data;
4 | this.next = next;
5 | }
6 |
7 | str() {
8 | let str = "";
9 | str = str + this.data;
10 |
11 | if this.next != null {
12 | str = str + " -> " + this.next.str();
13 | }
14 |
15 | ret str;
16 | }
17 | }
18 |
19 | let head = Node(1, Node(2, Node(3, Node(4, Node(5, null)))));
20 | print head.str();
21 |
22 |
--------------------------------------------------------------------------------
/examples/typechecking.nak:
--------------------------------------------------------------------------------
1 | class Bar {
2 | foo() {
3 | print("foo!");
4 | }
5 | }
6 |
7 | func useBar(bar: Bar) {
8 | print(bar);
9 | bar.foo();
10 | }
11 |
12 | useBar(Bar());
13 |
14 | func myFunc(a: int, b: int) -> int {
15 | ret a + b;
16 | }
17 |
18 | class Consumer {
19 | consume(callee: (int, int) -> int, a: int, b: int) -> int {
20 | ret callee(a,b);
21 | }
22 | }
23 |
24 | print(Consumer().consume(myFunc, 1, 2));
25 |
--------------------------------------------------------------------------------
/crates/interpreter/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "interpreter"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7 |
8 | [dependencies]
9 | parser = { path = "../parser" }
10 | ast = { path = "../ast" }
11 | meta = { path = "../meta" }
12 | miette = { version = "4.7.0" }
13 | thiserror = "1.0.31"
14 |
15 | [features]
16 | default = []
17 | trace = []
18 |
--------------------------------------------------------------------------------
/crates/nakala/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "nakala"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7 |
8 | [dependencies]
9 | parser = { path = "../parser" }
10 | interpreter = { path = "../interpreter" }
11 | ast = { path = "../ast" }
12 | compiler = { path = "../compiler" }
13 | meta = { path = "../meta" }
14 | miette = { version = "4.7.0", features = ["fancy"] }
15 | reedline = "0.5.0"
16 |
--------------------------------------------------------------------------------
/crates/parser/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "parser"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7 |
8 | [dependencies]
9 | lexer = { path = "../lexer" }
10 | ast = { path = "../ast" }
11 | miette = { version = "4.7.0" }
12 | meta = { path = "../meta" }
13 | thiserror = "1.0.31"
14 |
15 | [dev-dependencies]
16 | expect-test = "1.2.2"
17 |
18 | [features]
19 | default = []
20 | trace = []
21 |
--------------------------------------------------------------------------------
/crates/wasm/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "nakjs"
3 | version = "0.2.0"
4 | edition = "2021"
5 | license = "MIT"
6 | description = "JS bindings (via WASM) for the Nakala programming language"
7 |
8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
9 |
10 | [lib]
11 | crate-type = ["cdylib"]
12 |
13 | [dependencies]
14 | parser = { path = "../parser" }
15 | interpreter = { path = "../interpreter" }
16 | ast = { path = "../ast" }
17 | miette = { version = "4.7.0", features = ["fancy"] }
18 | meta = { path = "../meta" }
19 | wasm-bindgen = "0.2"
20 |
--------------------------------------------------------------------------------
/crates/wasm/README.md:
--------------------------------------------------------------------------------
1 | # nakjs
2 |
3 | JS Bindings for the Nakala programming language interpreter, written in Rust.
4 |
5 |
6 |
7 |
8 |
9 | > Note: This is extremely WIP
10 |
11 |
12 | [Visit here](https://github.com/nakala-lang/nakala) to learn more about nakala
13 |
14 | ## Devlopment
15 |
16 | [Follow this tutorial](https://developer.mozilla.org/en-US/docs/WebAssembly/Rust_to_wasm)
17 |
18 | ## Publishing
19 |
20 | ```console
21 | wasm-pack build --target bundler
22 | cd pkg
23 | npm publish
24 | ```
25 |
--------------------------------------------------------------------------------
/examples/ifs.nak:
--------------------------------------------------------------------------------
1 | if true {
2 | print "inside if";
3 | }
4 |
5 | if false {
6 | print "SHOULDNT BE HERE";
7 | } else {
8 | print "inside else";
9 | }
10 |
11 | if false {
12 | print "SHOULDNT BE HERE";
13 | } else if false {
14 | print "SHOULDNT BE HERE";
15 | } else if true {
16 | print "inside chained else if";
17 | }
18 |
19 | // Logical expressions short circuit
20 | if true and false {
21 | print "21: SHOULDNT BE HERE";
22 | }
23 |
24 | func foo() -> int {
25 | print "SHORTCIRCUIT FAILED";
26 | ret 5;
27 | }
28 |
29 | // Since we short circuit 'and' statements, this shouldn't
30 | if false and foo() {
31 | print "SHOULDNT BE HERE";
32 | }
33 |
34 | if false or foo() {
35 | print "inside or";
36 | }
37 |
--------------------------------------------------------------------------------
/examples/functions.nak:
--------------------------------------------------------------------------------
1 | // Functions don't have to return things
2 | func foo() {
3 | print "inside foo!";
4 | }
5 |
6 | // This function returns stuff
7 | func bar() {
8 | ret 10;
9 | }
10 |
11 | // Functions can be passed to other functions
12 | func identity(a: int) -> int {
13 | ret a;
14 | }
15 |
16 | func computeClosure(x, myArg) -> int {
17 | ret x(myArg);
18 | }
19 |
20 | print "The next value should be 5:";
21 | print computeClosure(identity, 5);
22 |
23 | // Functions can be declared inside functions
24 | func outside() -> string {
25 | let x = "the meaning of life";
26 |
27 | func inner() -> string {
28 | ret x;
29 | }
30 |
31 | ret inner();
32 | }
33 |
34 | print "The next value should be the meaning of life";
35 | print outside();
36 |
--------------------------------------------------------------------------------
/examples/class.nak:
--------------------------------------------------------------------------------
1 | class Limits {
2 | static INT_MAX = 32;
3 | }
4 |
5 | class Calculator {
6 | add(a: int, b: int) -> int {
7 | ret a + b;
8 | }
9 |
10 | add5(a: int) -> int {
11 | ret a + 5;
12 | }
13 |
14 | callWith10(a: any) {
15 | ret a(10);
16 | }
17 |
18 | int_is_in_bounds(a: int) -> bool {
19 | ret a <= Limits.INT_MAX;
20 | }
21 | }
22 |
23 | let calculator = Calculator();
24 | print(calculator);
25 |
26 | calculator.x = 10;
27 | print(calculator.x);
28 |
29 | let result = calculator.add(1,2);
30 | print(result);
31 |
32 | print(calculator.callWith10(calculator.add5));
33 |
34 | print("Next thing should be true:");
35 | print(calculator.int_is_in_bounds(10));
36 |
37 | print("Next thing should be false:");
38 | print(calculator.int_is_in_bounds(33));
39 |
--------------------------------------------------------------------------------
/examples/stack.nak:
--------------------------------------------------------------------------------
1 | class Stack {
2 | constructor() {
3 | this.inner = [];
4 | }
5 |
6 | push(a: int) {
7 | this.inner = this.inner + [a];
8 | }
9 |
10 | pop() -> int {
11 | if len(this.inner) == 0 {
12 | ret -1;
13 | }
14 | let item = this.inner[len(this.inner) - 1];
15 | let copy = [0; len(this.inner) - 1];
16 | let i = 0;
17 | until i == len(copy) {
18 | copy[i] = this.inner[i];
19 | i = i + 1;
20 | }
21 | this.inner = copy;
22 |
23 | ret item;
24 | }
25 |
26 | str() -> string {
27 | ret this.inner;
28 | }
29 | }
30 |
31 | let stack = Stack();
32 |
33 | let COUNT = 1000;
34 | let i = 0;
35 | until i == COUNT {
36 | stack.push(i);
37 | i = i + 1;
38 | }
39 |
40 | print(stack.str());
41 | until i == 0 {
42 | stack.pop();
43 | i = i - 1;
44 | }
45 |
46 | print(stack);
47 |
--------------------------------------------------------------------------------
/.github/workflows/CI.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 | pull_request:
7 | branches: [ main ]
8 |
9 | env:
10 | CARGO_TERM_COLOR: always
11 |
12 | jobs:
13 | lint:
14 | runs-on: ubuntu-latest
15 | steps:
16 | - name: Install rust
17 | uses: actions-rs/toolchain@v1
18 | with:
19 | profile: minimal
20 | toolchain: stable
21 | override: true
22 | components: rustfmt, clippy
23 | - name: Checkout
24 | uses: actions/checkout@v2
25 | - name: Clippy for all features
26 | uses: actions-rs/cargo@v1
27 | with:
28 | command: clippy
29 | args: -- -D warnings
30 | - name: Format check
31 | uses: actions-rs/cargo@v1
32 | with:
33 | command: fmt
34 | args: -- --check
35 |
36 | build_and_test:
37 | runs-on: ubuntu-latest
38 | needs: [lint]
39 | steps:
40 | - uses: actions/checkout@v2
41 | - name: Build
42 | run: cargo build --verbose
43 | - name: Run tests
44 | run: cargo test --verbose
45 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) [2021] [Reagan McFarland]
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.O
22 |
--------------------------------------------------------------------------------
/crates/interpreter/src/value/instance.rs:
--------------------------------------------------------------------------------
1 | use std::collections::HashMap;
2 |
3 | use crate::error::RuntimeError;
4 |
5 | use super::{class::Class, Value};
6 |
7 | pub type InstanceId = usize;
8 |
9 | #[derive(Debug, Clone, PartialEq)]
10 | pub struct Instance {
11 | id: InstanceId,
12 | class: Class,
13 | fields: HashMap,
14 | }
15 |
16 | impl Instance {
17 | pub fn new(id: InstanceId, class: Class) -> Self {
18 | // add all the methods to the fields
19 | let mut fields = HashMap::default();
20 | for (key, value) in class.methods.clone() {
21 | fields.insert(key, value);
22 | }
23 |
24 | Self { id, class, fields }
25 | }
26 |
27 | pub fn get_property(&self, name: &str) -> Result {
28 | if let Some(entry) = self.fields.get(name) {
29 | Ok(entry.clone())
30 | } else {
31 | Err(RuntimeError::UndefinedClassProperty(
32 | self.class.class.name.span.source_id,
33 | self.class.class.name.span.into(),
34 | name.to_string(),
35 | ))
36 | }
37 | }
38 |
39 | pub fn set_property(&mut self, name: String, val: Value) -> Result {
40 | self.fields.insert(name, val);
41 |
42 | Ok(Value::null())
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/crates/ast/src/op.rs:
--------------------------------------------------------------------------------
1 | use lexer::{Token, TokenKind};
2 | use meta::Span;
3 |
4 | #[derive(Debug, Copy, Clone, PartialEq)]
5 | pub enum Op {
6 | Equals,
7 | NotEquals,
8 | LessThan,
9 | LessThanEquals,
10 | GreaterThan,
11 | GreaterThanEquals,
12 | Add,
13 | Sub,
14 | Mul,
15 | Div,
16 | And,
17 | Or,
18 | Not,
19 | }
20 |
21 | #[derive(Debug, Copy, Clone, PartialEq)]
22 | pub struct Operator {
23 | pub op: Op,
24 | pub span: Span,
25 | }
26 |
27 | impl<'a> From<&Token> for Operator {
28 | fn from(token: &Token) -> Self {
29 | let op = match token.kind {
30 | TokenKind::Bang => Op::Not,
31 | TokenKind::EqualEqual => Op::Equals,
32 | TokenKind::BangEqual => Op::NotEquals,
33 | TokenKind::Less => Op::LessThan,
34 | TokenKind::LessEqual => Op::LessThanEquals,
35 | TokenKind::Greater => Op::GreaterThan,
36 | TokenKind::GreaterEqual => Op::GreaterThanEquals,
37 | TokenKind::Plus => Op::Add,
38 | TokenKind::Minus => Op::Sub,
39 | TokenKind::Star => Op::Mul,
40 | TokenKind::Slash => Op::Div,
41 | TokenKind::And => Op::And,
42 | TokenKind::Or => Op::Or,
43 | _ => unreachable!("ICE : Tried to convert non-op token into Op enum"),
44 | };
45 |
46 | Self {
47 | op,
48 | span: token.span,
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/crates/lexer/src/lib.rs:
--------------------------------------------------------------------------------
1 | use logos::Logos;
2 | use meta::{SourceId, Span, Spanned};
3 | use std::ops::Range as StdRange;
4 |
5 | mod token_kind;
6 | pub use token_kind::TokenKind;
7 |
8 | #[derive(Debug, PartialEq, Clone)]
9 | pub struct Token {
10 | pub kind: TokenKind,
11 | pub text: String,
12 | pub span: Span,
13 | }
14 |
15 | impl From<&Token> for Spanned {
16 | fn from(token: &Token) -> Self {
17 | Spanned {
18 | item: token.text.clone(),
19 | span: token.span,
20 | }
21 | }
22 | }
23 |
24 | impl From for Spanned {
25 | fn from(token: Token) -> Self {
26 | Spanned {
27 | item: token.text.clone(),
28 | span: token.span,
29 | }
30 | }
31 | }
32 |
33 | pub struct Lexer<'a> {
34 | source_id: SourceId,
35 | inner: logos::Lexer<'a, TokenKind>,
36 | }
37 |
38 | impl<'a> Lexer<'a> {
39 | pub fn new(source_id: SourceId, input: &'a str) -> Self {
40 | Self {
41 | source_id,
42 | inner: TokenKind::lexer(input),
43 | }
44 | }
45 | }
46 |
47 | impl<'a> Iterator for Lexer<'a> {
48 | type Item = Token;
49 |
50 | fn next(&mut self) -> Option {
51 | let kind = self.inner.next()?;
52 | let text = self.inner.slice();
53 |
54 | let span = {
55 | let StdRange { start, end } = self.inner.span();
56 |
57 | Span::new(self.source_id, start, end)
58 | };
59 |
60 | Some(Self::Item {
61 | kind,
62 | text: text.into(),
63 | span,
64 | })
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/crates/ast/src/stmt.rs:
--------------------------------------------------------------------------------
1 | use crate::{
2 | expr::Expression,
3 | ty::{Type, TypeExpression},
4 | };
5 | use lexer::Token;
6 | use meta::{Span, Spanned};
7 |
8 | #[derive(Debug, Clone, PartialEq)]
9 | pub struct Binding {
10 | pub name: Spanned,
11 | pub ty: Type,
12 | }
13 |
14 | impl From<&Token> for Binding {
15 | fn from(token: &Token) -> Self {
16 | Self {
17 | name: Spanned {
18 | item: token.text.to_string(),
19 | span: token.span,
20 | },
21 | ty: Type::Any,
22 | }
23 | }
24 | }
25 |
26 | #[derive(Debug, Clone, PartialEq)]
27 | pub struct Function {
28 | pub name: Spanned,
29 | pub params: Vec,
30 | pub body: Box,
31 | pub ty: TypeExpression,
32 | }
33 |
34 | #[derive(Debug, Clone, PartialEq)]
35 | pub struct Class {
36 | pub name: Spanned,
37 | pub methods: Vec,
38 | pub statics: Vec,
39 | }
40 |
41 | #[derive(Debug, Clone, PartialEq)]
42 | pub enum Stmt {
43 | Expr(Expression),
44 | Function(Function),
45 | Class(Class),
46 | Return(Option),
47 | Variable {
48 | name: Binding,
49 | expr: Option,
50 | },
51 | Block(Vec),
52 | If {
53 | cond: Expression,
54 | body: Box,
55 | else_branch: Option>,
56 | },
57 | Until {
58 | cond: Expression,
59 | body: Box,
60 | },
61 | }
62 |
63 | #[derive(Debug, Clone, PartialEq)]
64 | pub struct Statement {
65 | pub stmt: Stmt,
66 | pub span: Span,
67 | }
68 |
--------------------------------------------------------------------------------
/crates/interpreter/src/value/list.rs:
--------------------------------------------------------------------------------
1 | use super::Indexible;
2 | use crate::{env::Environment, error::RuntimeError, Val, Value};
3 | use ast::ty::Type;
4 | use meta::Span;
5 |
6 | pub type ListId = usize;
7 |
8 | #[derive(Debug, Clone, PartialEq)]
9 | pub struct List {
10 | id: ListId,
11 | values: Vec,
12 | }
13 |
14 | impl List {
15 | pub fn new(id: ListId, values: Vec) -> Self {
16 | Self { id, values }
17 | }
18 |
19 | pub fn to_string(&self, env: &mut Environment) -> String {
20 | format!(
21 | "[{}]",
22 | self.values
23 | .clone()
24 | .into_iter()
25 | .map(|val| val.to_string(env))
26 | .collect::>()
27 | .join(",")
28 | )
29 | }
30 |
31 | pub fn len(&self) -> Value {
32 | Value {
33 | val: Val::Int(self.values.len() as i64),
34 | span: Span::garbage(),
35 | ty: Type::Int,
36 | }
37 | }
38 |
39 | pub fn extend_with(&mut self, other: Self) {
40 | self.values.extend(other.values);
41 | }
42 | }
43 |
44 | impl Indexible for List {
45 | fn get(&self, index: usize) -> Result {
46 | if let Some(val) = self.values.get(index) {
47 | Ok(val.clone())
48 | } else {
49 | todo!("not found");
50 | }
51 | }
52 |
53 | fn set(&mut self, index: usize, val: Value) -> Result<(), RuntimeError> {
54 | if index >= self.values.len() {
55 | todo!("out of bounds");
56 | } else {
57 | self.values[index] = val;
58 | Ok(())
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/crates/ast/src/expr.rs:
--------------------------------------------------------------------------------
1 | use crate::{op::Operator, ty::Type};
2 | use meta::{Span, Spanned};
3 |
4 | #[derive(Debug, Clone, PartialEq)]
5 | pub enum Expr {
6 | Bool(bool),
7 | Int(i64),
8 | Float(f64),
9 | String(String),
10 | Null,
11 | Unary {
12 | op: Operator,
13 | rhs: Box,
14 | },
15 | Binary {
16 | lhs: Box,
17 | op: Operator,
18 | rhs: Box,
19 | },
20 | Grouping(Box),
21 | Variable(String),
22 | Assign {
23 | name: Spanned,
24 | rhs: Box,
25 | },
26 | // Logical expressions short circuit, unlike Binary
27 | Logical {
28 | lhs: Box,
29 | op: Operator,
30 | rhs: Box,
31 | },
32 | Call {
33 | callee: Box,
34 | paren: Span,
35 | args: Vec,
36 | },
37 | Get {
38 | object: Box,
39 | name: Spanned,
40 | },
41 | Set {
42 | object: Box,
43 | name: Spanned,
44 | rhs: Box,
45 | },
46 | IndexGet {
47 | lhs: Box,
48 | index: Box,
49 | },
50 | IndexSet {
51 | lhs: Box,
52 | index: Box,
53 | rhs: Box,
54 | },
55 | List(Vec),
56 | ListShorthand {
57 | value: Box,
58 | count: Box,
59 | },
60 | This,
61 | }
62 |
63 | #[derive(Debug, Clone, PartialEq)]
64 | pub struct Expression {
65 | pub expr: Expr,
66 | pub span: Span,
67 | pub ty: Type,
68 | }
69 |
--------------------------------------------------------------------------------
/crates/interpreter/src/value/class.rs:
--------------------------------------------------------------------------------
1 | use std::collections::HashMap;
2 |
3 | use ast::{expr::Expression, stmt::Class as AstClass};
4 | use meta::Span;
5 |
6 | use crate::{
7 | env::{Environment, ScopeId},
8 | error::RuntimeError,
9 | };
10 |
11 | use super::{Callable, Value};
12 |
13 | #[derive(Debug, Clone, PartialEq)]
14 | pub struct Class {
15 | pub class: AstClass,
16 | pub methods: HashMap,
17 | pub statics: HashMap,
18 | }
19 |
20 | impl Class {
21 | pub fn constructor(&self) -> Option {
22 | self.methods.get("constructor").cloned()
23 | }
24 | }
25 |
26 | impl Callable for Class {
27 | fn arity(&self) -> usize {
28 | self.constructor().map_or(0, |func| {
29 | func.as_function()
30 | .expect("constructor has to be a function")
31 | .arity()
32 | })
33 | }
34 |
35 | fn call(
36 | &self,
37 | callee_span: Span,
38 | args: Vec,
39 | env: &mut Environment,
40 | scope: ScopeId,
41 | ) -> Result {
42 | if self.arity() != args.len() {
43 | return Err(RuntimeError::ArityMismatch(
44 | callee_span.source_id,
45 | callee_span.into(),
46 | self.arity(),
47 | args.len(),
48 | ));
49 | }
50 |
51 | let val = env.new_instance(self.clone(), Span::garbage());
52 | let instance = env.get_instance(val.as_instance()?)?;
53 | if let Ok(mut constructor) = instance.get_property("constructor") {
54 | // bind this and execute constructor
55 | constructor.bind_this(env, val.clone())?;
56 | constructor
57 | .as_function()?
58 | .call(callee_span, args, env, scope)?;
59 | }
60 |
61 | Ok(val)
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/examples/rule110.nak:
--------------------------------------------------------------------------------
1 | class List {
2 | constructor(size: int) {
3 | this.inner = [0; size];
4 | }
5 |
6 | set(index: int, value: int) {
7 | this.inner[index] = value;
8 | }
9 |
10 | get(index: int) -> int {
11 | ret this.inner[index];
12 | }
13 |
14 | str() -> string {
15 | let s = "";
16 |
17 | let i = 0;
18 | //print("size of list is " + len(this.inner));
19 | until i == len(this.inner) {
20 | if this.inner[i] == 0 {
21 | s = s + " ";
22 | } else {
23 | s = s + "X";
24 | }
25 |
26 | i = i + 1;
27 | }
28 |
29 | ret s;
30 | }
31 | }
32 |
33 | let LIST_SIZE = 100;
34 |
35 | let list = List(LIST_SIZE);
36 | list.set(LIST_SIZE - 2, 1);
37 | list.set(LIST_SIZE - 1, 1);
38 |
39 | let iter = 0;
40 | until iter == LIST_SIZE {
41 | print(list.str());
42 | iter = iter + 1;
43 |
44 | //print("creating new list");
45 | let new_list = List(LIST_SIZE);
46 | //print("finished creating new list");
47 |
48 | let idx = 0;
49 | until idx == LIST_SIZE {
50 | let prev_val = 0;
51 | if idx != 0 {
52 | prev_val = list.get(idx - 1);
53 | }
54 |
55 | let curr_val = list.get(idx);
56 |
57 | let next_val = 0;
58 | if idx != LIST_SIZE - 1 {
59 | next_val = list.get(idx + 1);
60 | }
61 |
62 | let as_str = "" + prev_val + curr_val + next_val;
63 | let new_val = 0;
64 | if as_str == "111" {
65 | new_val = 0;
66 | } else if as_str == "110" {
67 | new_val = 1;
68 | } else if as_str == "101" {
69 | new_val = 1;
70 | } else if as_str == "100" {
71 | new_val = 0;
72 | } else if as_str == "011" {
73 | new_val = 1;
74 | } else if as_str == "010" {
75 | new_val = 1;
76 | } else if as_str == "001" {
77 | new_val = 1;
78 | } else {
79 | new_val = 0;
80 | }
81 |
82 | new_list.set(idx, new_val);
83 |
84 | idx = idx + 1;
85 | }
86 |
87 | list = new_list;
88 | }
89 |
--------------------------------------------------------------------------------
/crates/meta/src/span.rs:
--------------------------------------------------------------------------------
1 | use miette::SourceSpan;
2 |
3 | use crate::SourceId;
4 |
5 | #[derive(Clone, Debug, PartialEq)]
6 | pub struct Spanned
7 | where
8 | T: Clone + std::fmt::Debug,
9 | {
10 | pub item: T,
11 | pub span: Span,
12 | }
13 |
14 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
15 | pub struct Span {
16 | pub source_id: SourceId,
17 | pub start: usize,
18 | pub end: usize,
19 | }
20 |
21 | impl From for SourceSpan {
22 | fn from(s: Span) -> Self {
23 | Self::new(s.start.into(), (s.end - s.start).into())
24 | }
25 | }
26 |
27 | impl Span {
28 | pub fn new(source_id: SourceId, start: usize, end: usize) -> Self {
29 | Span {
30 | source_id,
31 | start,
32 | end,
33 | }
34 | }
35 |
36 | pub fn garbage() -> Self {
37 | Self {
38 | source_id: 0,
39 | start: 0,
40 | end: 0,
41 | }
42 | }
43 |
44 | pub fn combine(spans: &[Span]) -> Self {
45 | let length = spans.len();
46 |
47 | if length == 0 {
48 | Span::garbage()
49 | } else if length == 1 {
50 | spans[0]
51 | } else {
52 | Self {
53 | source_id: spans[0].source_id,
54 | start: spans[0].start,
55 | end: spans[length - 1].end,
56 | }
57 | }
58 | }
59 |
60 | pub fn offset(&self, offset: usize) -> Self {
61 | Span {
62 | source_id: self.source_id,
63 | start: self.start - offset,
64 | end: self.end - offset,
65 | }
66 | }
67 |
68 | pub fn contains(&self, pos: usize) -> bool {
69 | pos >= self.start && pos < self.end
70 | }
71 |
72 | pub fn past(&self) -> Span {
73 | Span {
74 | source_id: self.source_id,
75 | start: self.end,
76 | end: self.end,
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/crates/interpreter/src/value/function.rs:
--------------------------------------------------------------------------------
1 | use ast::{expr::Expression, stmt::Function as AstFunction, ty::type_compatible};
2 | use meta::Span;
3 |
4 | use crate::{
5 | env::{Environment, ScopeId},
6 | error::RuntimeError,
7 | eval_block,
8 | expr::eval_expr,
9 | };
10 |
11 | use super::{Callable, Value};
12 |
13 | #[derive(Debug, Clone, PartialEq)]
14 | pub struct Function {
15 | pub func: AstFunction,
16 | pub closure: ScopeId,
17 | }
18 |
19 | impl Callable for Function {
20 | fn arity(&self) -> usize {
21 | self.func.params.len()
22 | }
23 |
24 | fn call(
25 | &self,
26 | callee_span: Span,
27 | args: Vec,
28 | env: &mut Environment,
29 | scope: ScopeId,
30 | ) -> Result {
31 | if self.arity() != args.len() {
32 | return Err(RuntimeError::ArityMismatch(
33 | callee_span.source_id,
34 | callee_span.into(),
35 | self.arity(),
36 | args.len(),
37 | ));
38 | }
39 |
40 | let new_scope = env.begin_scope(self.closure);
41 |
42 | let params = &self.func.params;
43 | for (param, arg) in params.iter().zip(args.into_iter()) {
44 | // TODO try to move this to the parser
45 | if !type_compatible(¶m.ty, &arg.ty) {
46 | return Err(RuntimeError::IncompatibleTypes(
47 | param.name.span.source_id,
48 | param.name.span.into(),
49 | param.ty.clone(),
50 | arg.span.into(),
51 | arg.ty,
52 | ));
53 | }
54 | let val = eval_expr(arg, env, scope)?;
55 | env.define(new_scope, param.name.item.clone(), val)?;
56 | }
57 |
58 | match eval_block(*self.func.body.clone(), env, new_scope) {
59 | Ok(()) => Ok(Value::null()),
60 | Err(RuntimeError::EarlyReturn(val)) => Ok(val),
61 |
62 | Err(other) => Err(other),
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | [](https://github.com/nakala-lang/nakala/actions/workflows/CI.yml)
6 | 
7 | [](https://github.com/nakala-lang/nakala/blob/master/LICENSE)
8 |
9 |
10 | ### Why make nakala?
11 |
12 | > Note: see _History_
13 |
14 | After learning about progrmaming languages for over a year (including books, college level courses, multiple side projects, etc.), I've decided I want to make my own, general purpose programming language.
15 |
16 | ### Philosophy
17 |
18 | Nakala only has two core values:
19 |
20 | 1. The syntax should feel familiar, while remaining unique
21 | 2. Nakala doesn't try to do anything _really hard_, as I want to make working on this project fun and not another job that ends up burning me out.
22 |
23 | ### Roadmap
24 |
25 | - [x] Lexer
26 | - [x] Parser / AST
27 | - [x] Type Checking
28 | - [x] Basic types
29 | - [x] Advanced types (functions, classes, enums)
30 | - [x] Interpreter
31 | - [ ] Standard library
32 | - [x] [Playground website](https://nakala-lang.github.io/website/)
33 | - [ ] Editor tooling
34 | - [x] [vim](https://github.com/nakala-lang/nakala.vim)
35 | - [ ] Compiler (maybe C++?)
36 | - [ ] Self hosted
37 |
38 | ### Contributing
39 | I am always welcoming PRs and would love to work on the project with other people if they are interested. There are no rules, and I will accept any PR as long as it aligns with the projects core values as described above.
40 |
41 | ---
42 |
43 | #### History
44 |
45 | Nakala started as a programming language that I built based on [arzg's Eldiro blog posts](https://arzg.github.io/lang/).
46 | The core architecture (especially the Parser) was practically identical to Eldiro, hence the name of the project: nakala (Punjabi for _mimic_).
47 | If you haven't read through his blog posts, I highly recommend you do because it is an unmatched learning resource.
48 |
49 | I ended up scrapping the entire language and implementation for something I built from the ground up after studying more. However, I decided to keep the name as a nod to arzg for lighting a spark in me that put me on a journey of learning constantly.
50 |
51 | #### License
52 | `nakala` uses the MIT License
53 |
--------------------------------------------------------------------------------
/crates/wasm/src/lib.rs:
--------------------------------------------------------------------------------
1 | use ast::ty::Type;
2 | use interpreter::{env::Environment, error::RuntimeError, interpret, Builtin, Val, Value};
3 | use parser::{parse, source::Source, SymbolTable};
4 | use wasm_bindgen::{prelude::*, JsValue};
5 |
6 | static mut OUTPUT: String = String::new();
7 |
8 | #[wasm_bindgen]
9 | pub fn wasm_interpret(source: &str) -> JsValue {
10 | unsafe {
11 | OUTPUT = String::new();
12 | }
13 |
14 | match helper(source) {
15 | Ok(_) => unsafe { JsValue::from_str(&format!("[Finished]\n{}", OUTPUT)) },
16 | Err(e) => unsafe {
17 | OUTPUT.push_str(&format!("{:?}", e));
18 | JsValue::from_str(&OUTPUT)
19 | },
20 | }
21 | }
22 |
23 | pub fn helper(source: &str) -> miette::Result<()> {
24 | let mut builtins = vec![];
25 |
26 | fn print(vals: Vec, env: &mut Environment) -> Result {
27 | unsafe {
28 | OUTPUT.push_str(
29 | &vals.first()
30 | .expect("arity mismatch didn't catch builtin")
31 | .to_string(env)
32 | );
33 | OUTPUT.push('\n');
34 | }
35 | Ok(Value::null())
36 | }
37 | builtins.push(Builtin::new(
38 | String::from("print"),
39 | vec![Type::Any],
40 | None,
41 | print,
42 | ));
43 |
44 | //len
45 | fn len(vals: Vec, env: &mut Environment) -> Result {
46 | let val = vals.first().expect("arity mismatch didn't catch builtin");
47 | match val.val {
48 | Val::List { id } => Ok(env.get_list(id).len()),
49 | _ => todo!(""),
50 | }
51 | }
52 | builtins.push(Builtin::new(
53 | String::from("len"),
54 | vec![Type::Any],
55 | Some(Type::Int),
56 | len,
57 | ));
58 |
59 | let symbols = builtins
60 | .clone()
61 | .into_iter()
62 | .map(|b| b.as_symbol())
63 | .collect();
64 |
65 | let mut env = Environment::new(builtins).expect("ICE: failed to create Environment");
66 | let symtab = SymbolTable::new(symbols);
67 |
68 | let source = Source::new(0, String::from(source), "stdin".to_string());
69 | let parse =
70 | parse(source.clone(), symtab).map_err(|error| error.with_source_code(source.clone()))?;
71 |
72 | interpret(parse, &mut env).map_err(|error| error.with_source_code(source.clone()))?;
73 |
74 | Ok(())
75 | }
76 |
--------------------------------------------------------------------------------
/crates/interpreter/src/error.rs:
--------------------------------------------------------------------------------
1 | use ast::ty::Type;
2 | use meta::SourceId;
3 | use miette::{Diagnostic, SourceSpan};
4 | use thiserror::Error;
5 |
6 | use crate::value::Value;
7 |
8 | #[derive(Error, Diagnostic, Debug)]
9 | pub enum RuntimeError {
10 | // This should never bubble up all the way to the top, and the parser should prevent using
11 | // return in non function contexts
12 | #[error("Early Return")]
13 | EarlyReturn(Value),
14 |
15 | #[error("Arity mismatch")]
16 | #[diagnostic(code(nak_runtime::arity_mismatch))]
17 | ArityMismatch(
18 | SourceId,
19 | #[label("This function expects {2} arguments, but got {3}")] SourceSpan,
20 | usize,
21 | usize,
22 | ),
23 |
24 | #[error("Undefined variable")]
25 | #[diagnostic(code(nak_runtime::unknown_variable))]
26 | UndefinedVariable(SourceId, #[label("Undefined variable")] SourceSpan),
27 |
28 | #[error("Expected {1}, got {2} value instead")]
29 | #[diagnostic(code(nak_runtime::unexpected_value))]
30 | UnexpectedValueType(
31 | SourceId,
32 | Type,
33 | String,
34 | #[label("This value is not of type {1}")] SourceSpan,
35 | ),
36 |
37 | #[error("Unsupported operation")]
38 | #[diagnostic(code(nak_runtime::unexpected_operation))]
39 | UnsupportedOperation(
40 | SourceId,
41 | #[label("This operation doesn't support these types")] SourceSpan,
42 | #[label("{3}")] SourceSpan,
43 | Type,
44 | #[label("{5}")] SourceSpan,
45 | Type,
46 | ),
47 |
48 | #[error("Undefined class property")]
49 | #[diagnostic(code(nak_runtime::undefined_class_property))]
50 | UndefinedClassProperty(
51 | SourceId,
52 | #[label("This class doesn't have any property named {2}")] SourceSpan,
53 | String,
54 | ),
55 |
56 | #[error("Undefined static class property")]
57 | #[diagnostic(code(nak_runtime::undefined_static_property))]
58 | UndefinedStaticClassProperty(
59 | SourceId,
60 | #[label("This class doesn't have any static properties named {2}")] SourceSpan,
61 | String,
62 | ),
63 |
64 | #[error("Incompatible types")]
65 | #[diagnostic(
66 | code(nak::incompatible_types),
67 | help("This should have been caught by the parser")
68 | )]
69 | IncompatibleTypes(
70 | SourceId,
71 | #[label("Expects types compatible with {2}")] SourceSpan,
72 | Type,
73 | #[label("{4}")] SourceSpan,
74 | Type,
75 | ),
76 | }
77 |
--------------------------------------------------------------------------------
/crates/parser/src/source.rs:
--------------------------------------------------------------------------------
1 | use lexer::{Lexer, Token, TokenKind};
2 | use meta::SourceId;
3 | use miette::{MietteSpanContents, NamedSource, SourceCode, SourceSpan};
4 |
5 | #[derive(Debug, Clone)]
6 | pub struct Source {
7 | pub id: SourceId,
8 | raw: String,
9 | name: String,
10 | tokens: Vec,
11 | cursor: usize,
12 | }
13 |
14 | impl Source {
15 | pub fn new(id: SourceId, raw: String, name: String) -> Self {
16 | let tokens: Vec<_> = Lexer::new(id, &raw).collect();
17 | Self {
18 | id,
19 | raw,
20 | name,
21 | tokens,
22 | cursor: 0,
23 | }
24 | }
25 |
26 | pub fn next_token(&mut self) -> Option<&Token> {
27 | self.eat_trivia();
28 |
29 | let token = self.tokens.get(self.cursor)?;
30 | self.cursor += 1;
31 |
32 | Some(token)
33 | }
34 |
35 | pub fn peek_kind(&mut self) -> Option {
36 | self.eat_trivia();
37 | self.peek_kind_raw()
38 | }
39 |
40 | pub fn eof(&self) -> SourceSpan {
41 | (self.raw.len() - 1, 0).into()
42 | }
43 |
44 | pub fn at_end(&mut self) -> bool {
45 | self.eat_trivia();
46 | self.peek_token_raw().is_none()
47 | }
48 |
49 | fn eat_trivia(&mut self) {
50 | while self.at_trivia() {
51 | self.cursor += 1;
52 | }
53 | }
54 |
55 | fn at_trivia(&self) -> bool {
56 | self.peek_kind_raw().map_or(false, TokenKind::is_trivia)
57 | }
58 |
59 | fn peek_kind_raw(&self) -> Option {
60 | self.peek_token_raw().map(|Token { kind, .. }| *kind)
61 | }
62 |
63 | fn peek_token_raw(&self) -> Option<&Token> {
64 | self.tokens.get(self.cursor)
65 | }
66 | }
67 |
68 | impl SourceCode for Source {
69 | fn read_span<'a>(
70 | &'a self,
71 | span: &miette::SourceSpan,
72 | context_lines_before: usize,
73 | context_lines_after: usize,
74 | ) -> Result + 'a>, miette::MietteError> {
75 | let contents = self
76 | .raw
77 | .read_span(span, context_lines_before, context_lines_after)?;
78 | Ok(Box::new(MietteSpanContents::new_named(
79 | self.name.clone(),
80 | contents.data(),
81 | *contents.span(),
82 | contents.line(),
83 | contents.column(),
84 | contents.line_count(),
85 | )))
86 | }
87 | }
88 |
89 | impl From<&Source> for NamedSource {
90 | fn from(source: &Source) -> Self {
91 | let name = source.name.clone();
92 | let input = source.raw.clone();
93 |
94 | NamedSource::new(name, input)
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/crates/parser/src/symtab.rs:
--------------------------------------------------------------------------------
1 | use ast::ty::Type;
2 | use meta::Spanned;
3 | use std::{collections::HashMap, usize};
4 |
5 | #[derive(Debug, Clone, PartialEq)]
6 | pub struct Symbol {
7 | pub name: Spanned,
8 | pub sym: Sym,
9 | pub ty: Type,
10 | }
11 |
12 | #[derive(Debug, Clone, PartialEq)]
13 | pub enum Sym {
14 | Variable,
15 | Function {
16 | arity: usize,
17 | },
18 | Class {
19 | methods: HashMap,
20 | statics: HashMap,
21 | },
22 | }
23 |
24 | #[derive(Debug, Clone, PartialEq)]
25 | pub struct SymbolTable {
26 | inner: Vec>,
27 | }
28 |
29 | impl SymbolTable {
30 | pub fn new(builtins: Vec) -> Self {
31 | let mut symtab = SymbolTable {
32 | inner: vec![HashMap::default()],
33 | };
34 |
35 | symtab.define_builtins(builtins);
36 |
37 | symtab
38 | }
39 |
40 | fn define_builtins(&mut self, builtins: Vec) {
41 | for builtin in builtins {
42 | self.insert(builtin);
43 | }
44 | }
45 |
46 | pub fn merge_with(&mut self, other: SymbolTable) {
47 | //FIXME I'm pretty sure we only need to merge the first level of each since we use this
48 | //function for the REPL and never in a nested scope
49 | let self_map = self
50 | .inner
51 | .get_mut(0)
52 | .expect("symtabs must have at least one level");
53 | self_map.extend(
54 | other
55 | .inner
56 | .into_iter()
57 | .next()
58 | .expect("symtabs must have atleast one level"),
59 | );
60 | }
61 |
62 | pub fn at_global_scope(&self) -> bool {
63 | self.inner.len() == 1
64 | }
65 |
66 | pub fn level_up(&mut self) {
67 | self.inner.push(HashMap::default());
68 | }
69 |
70 | pub fn level_down(&mut self) {
71 | self.inner.pop();
72 | }
73 |
74 | pub fn insert(&mut self, sym: Symbol) {
75 | if let Some(map) = self.inner.last_mut() {
76 | map.insert(sym.name.item.clone(), sym);
77 | }
78 | }
79 |
80 | pub fn lookup(&self, name: &str) -> Option<&Symbol> {
81 | for map in self.inner.iter().rev() {
82 | if let Some(entry) = map.get(name) {
83 | return Some(entry);
84 | }
85 | }
86 |
87 | None
88 | }
89 |
90 | pub fn lookup_mut(&mut self, name: &str) -> Option<&mut Symbol> {
91 | for map in self.inner.iter_mut().rev() {
92 | if let Some(entry) = map.get_mut(name) {
93 | return Some(entry);
94 | }
95 | }
96 |
97 | None
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/crates/interpreter/src/value/builtin.rs:
--------------------------------------------------------------------------------
1 | use ast::{
2 | expr::Expression,
3 | ty::{type_compatible, Type, TypeExpression},
4 | };
5 | use meta::{Span, Spanned};
6 | use parser::{Sym, Symbol};
7 |
8 | use crate::{
9 | env::{Environment, ScopeId},
10 | error::RuntimeError,
11 | expr::eval_expr,
12 | };
13 |
14 | use super::{Callable, Value};
15 |
16 | // Builtins are only functions
17 | pub struct Builtin {
18 | pub name: String,
19 | pub ty: Type,
20 | pub handler: fn(Vec, &mut Environment) -> Result,
21 | }
22 |
23 | impl Builtin {
24 | pub fn new(
25 | name: String,
26 | params: Vec,
27 | returns: Option,
28 | handler: fn(Vec, &mut Environment) -> Result,
29 | ) -> Self {
30 | Self {
31 | name,
32 | handler,
33 | ty: Type::Function {
34 | params: params
35 | .into_iter()
36 | .map(|t| TypeExpression {
37 | ty: t,
38 | span: Span::garbage(),
39 | })
40 | .collect(),
41 | returns: Box::new(TypeExpression {
42 | span: Span::garbage(),
43 | ty: returns.unwrap_or(Type::Null),
44 | }),
45 | },
46 | }
47 | }
48 |
49 | pub fn as_symbol(&self) -> Symbol {
50 | if let Type::Function { params, .. } = &self.ty {
51 | Symbol {
52 | name: Spanned {
53 | item: self.name.clone(),
54 | span: Span::garbage(),
55 | },
56 | sym: Sym::Function {
57 | arity: params.len(),
58 | },
59 | ty: self.ty.clone(),
60 | }
61 | } else {
62 | panic!("ICE: builtin type is not a function signature");
63 | }
64 | }
65 | }
66 |
67 | impl Clone for Builtin {
68 | fn clone(&self) -> Self {
69 | Self {
70 | name: self.name.clone(),
71 | ty: self.ty.clone(),
72 | handler: self.handler,
73 | }
74 | }
75 | }
76 |
77 | impl std::fmt::Display for Builtin {
78 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
79 | write!(f, " {}", self.name)
80 | }
81 | }
82 |
83 | impl std::fmt::Debug for Builtin {
84 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
85 | write!(f, " {}", self.name)
86 | }
87 | }
88 |
89 | impl Callable for Builtin {
90 | fn arity(&self) -> usize {
91 | if let Type::Function { params, .. } = &self.ty {
92 | params.len()
93 | } else {
94 | panic!("builtin type is not a function signature");
95 | }
96 | }
97 |
98 | fn call(
99 | &self,
100 | callee_span: Span,
101 | args: Vec,
102 | env: &mut Environment,
103 | scope: ScopeId,
104 | ) -> Result {
105 | if self.arity() != args.len() {
106 | return Err(RuntimeError::ArityMismatch(
107 | callee_span.source_id,
108 | callee_span.into(),
109 | self.arity(),
110 | args.len(),
111 | ));
112 | }
113 |
114 | if let Type::Function { params, .. } = &self.ty {
115 | let mut vals = vec![];
116 | for (param, arg) in params.iter().zip(args.into_iter()) {
117 | if !type_compatible(&arg.ty, ¶m.ty) {
118 | todo!("runtime builtin type mismatch");
119 | }
120 | vals.push(eval_expr(arg, env, scope)?);
121 | }
122 |
123 | let mut val = (self.handler)(vals, env)?;
124 | val.span = Span::combine(&[callee_span, val.span]);
125 | Ok(val)
126 | } else {
127 | panic!("builtin type is not a function signature");
128 | }
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/crates/compiler/src/lib.rs:
--------------------------------------------------------------------------------
1 | use ast::{
2 | expr::{Expr, Expression},
3 | op::{Operator},
4 | stmt::{Statement, Stmt},
5 | };
6 | use miette::Diagnostic;
7 | use parser::Parse;
8 | use thiserror::Error;
9 |
10 | #[derive(Error, Debug, Diagnostic)]
11 | pub enum CompileError {}
12 |
13 | pub fn compile(parse: Parse) -> miette::Result {
14 | let mut output = String::with_capacity(8192);
15 | add_prelude(&mut output);
16 | output.push_str("int main(int argc, char** argv) {");
17 |
18 | for stmt in parse.stmts {
19 | output.push_str(&stmt.codegen()?);
20 | }
21 |
22 | output.push('}');
23 |
24 | Ok(output)
25 | }
26 |
27 | trait Codegen {
28 | fn codegen(&self) -> Result;
29 | }
30 |
31 | impl Codegen for Statement {
32 | fn codegen(&self) -> Result {
33 | match &self.stmt {
34 | Stmt::Variable { name, expr } => {
35 | let mut res = format!("Value {} = ", name.name.item);
36 |
37 | if let Some(expr) = expr {
38 | res.push_str(&expr.codegen()?);
39 | } else {
40 | res.push_str("Value::null()");
41 | }
42 |
43 | res.push(';');
44 |
45 | Ok(res)
46 | }
47 | Stmt::Expr(expr) => Ok(format!("{};", expr.codegen()?)),
48 | _ => todo!("codegen for {:?}", self),
49 | }
50 | }
51 | }
52 |
53 | impl Codegen for Expression {
54 | fn codegen(&self) -> Result {
55 | match &self.expr {
56 | Expr::Bool(v) => Ok(format!("Value((bool) {})", v)),
57 | Expr::Int(v) => Ok(format!("Value((int64_t) {})", v)),
58 | Expr::Float(v) => Ok(format!("Value((float) {})", v)),
59 | Expr::String(v) => Ok(format!(r#"Value(std::string("{}"))"#, v)),
60 | Expr::Variable(name) => Ok(name.to_string()),
61 | Expr::Call { callee, args, .. } => {
62 | match &callee.expr {
63 | // TODO - deal with builtins better
64 | Expr::Variable(name) => {
65 | if name == "print" {
66 | Ok(format!("std::cout << {}", args.get(0).unwrap().codegen()?))
67 | } else {
68 | todo!("codegen for non print")
69 | }
70 | }
71 | _ => todo!("callee codegen for {:?}", self),
72 | }
73 | }
74 | _ => todo!("codegen for {:?}", self),
75 | }
76 | }
77 | }
78 |
79 | impl Codegen for Operator {
80 | fn codegen(&self) -> Result {
81 | todo!("codegen for {:?}", self)
82 | }
83 | }
84 |
85 | fn add_prelude(output: &mut String) {
86 | output.push_str(r#"
87 | #include
88 | #include
89 |
90 | class Value {
91 | public:
92 | enum class Type {
93 | Null,
94 | Int,
95 | Float,
96 | String,
97 | Bool,
98 | };
99 |
100 | bool is_null() const { return m_type == Type::Null; }
101 | bool is_int() const { return m_type == Type::Int; }
102 | bool is_float() const { return m_type == Type::Float; }
103 | bool is_string() const { return m_type == Type::String; }
104 | bool is_bool() const { return m_type == Type::Bool; }
105 |
106 | Value() : m_type(Type::Null) {}
107 |
108 | explicit Value(bool value) : m_type(Type::Bool) { m_value.as_bool = value; }
109 | explicit Value(int64_t value) : m_type(Type::Int) { m_value.as_int = value; }
110 | explicit Value(float value) : m_type(Type::Float) { m_value.as_float = value; }
111 | explicit Value(std::string value) : m_type(Type::String) { m_value.as_string = const_cast(value.c_str()); }
112 |
113 | std::string to_string() const {
114 | switch (m_type) {
115 | case Type::Null:
116 | return "null";
117 | case Type::Int:
118 | return std::to_string(m_value.as_int);
119 | case Type::Bool:
120 | return m_value.as_bool ? "true" : "false";
121 | case Type::String:
122 | return m_value.as_string;
123 | case Type::Float:
124 | return std::to_string(m_value.as_float);
125 | default:
126 | std::cout << "nyi" << std::endl;
127 | exit(2);
128 | }
129 | }
130 |
131 | private:
132 | Type m_type{Type::Null};
133 |
134 | union t {
135 | int64_t as_int;
136 | float as_float;
137 | bool as_bool;
138 | char *as_string;
139 | } m_value;
140 | };
141 |
142 | std::ostream &operator<<(std::ostream &Str, Value const &val) {
143 | Str << val.to_string() << std::endl;
144 | return Str;
145 | }"#);
146 | }
147 |
--------------------------------------------------------------------------------
/crates/parser/src/error.rs:
--------------------------------------------------------------------------------
1 | use ast::ty::Type;
2 | use lexer::TokenKind;
3 | use meta::SourceId;
4 | use miette::{Diagnostic, SourceSpan};
5 | use thiserror::Error;
6 |
7 | #[derive(Error, Diagnostic, Debug)]
8 | pub enum ParseError {
9 | #[error("Expected an expression")]
10 | #[diagnostic(
11 | code(nak::cant_parse_primary_expr),
12 | help("Change this into an expression")
13 | )]
14 | ExpectedExpression(SourceId, #[label] SourceSpan),
15 |
16 | #[error("Expected token '{2}', but found '{1}' instead")]
17 | #[diagnostic(code(nak::expected_token))]
18 | ExpectedToken(
19 | SourceId,
20 | String,
21 | TokenKind,
22 | #[label("Consider adding '{2}' here")] SourceSpan,
23 | ),
24 |
25 | #[error("Unexpected EOF")]
26 | #[diagnostic(
27 | code(nak::unexpected_eof),
28 | help("Expected more tokens, but none were found")
29 | )]
30 | UnexpectedEof(SourceId, #[label] SourceSpan),
31 |
32 | #[error("Invalid assignment target")]
33 | #[diagnostic(
34 | code(nak::invalid_assign_target),
35 | help("You can only assign to variables")
36 | )]
37 | InvalidAssignmentTarget(SourceId, #[label] SourceSpan),
38 |
39 | #[error("Unsupported operation")]
40 | #[diagnostic(code(nak::unsupported_operation))]
41 | UnsupportedOperation(
42 | SourceId,
43 | #[label("This operation doesn't support these types")] SourceSpan,
44 | #[label("{3}")] SourceSpan,
45 | Type,
46 | #[label("{5}")] SourceSpan,
47 | Type,
48 | ),
49 |
50 | #[error("Unsupported unary operation")]
51 | #[diagnostic(code(nak::unsupported_unary_operation))]
52 | UnsupportedUnaryOperation(
53 | SourceId,
54 | #[label("This operation doesn't support this type")] SourceSpan,
55 | #[label("{3}")] SourceSpan,
56 | Type,
57 | ),
58 |
59 | #[error("Undeclared variable")]
60 | #[diagnostic(
61 | code(nak::undeclared_variable),
62 | help("Consider adding 'let {2} = ...' before it's usage")
63 | )]
64 | UndeclaredVariable(
65 | SourceId,
66 | #[label("This variable has not been declared")] SourceSpan,
67 | String,
68 | ),
69 |
70 | #[error("Incompatible types")]
71 | #[diagnostic(code(nak::incompatible_types))]
72 | IncompatibleTypes(
73 | SourceId,
74 | #[label("Expects types compatible with {2}")] SourceSpan,
75 | Type,
76 | #[label("{4}")] SourceSpan,
77 | Type,
78 | ),
79 |
80 | #[error("Unknown type")]
81 | #[diagnostic(
82 | code(nak::unknown_type),
83 | help("Consider using 'int', 'float', 'bool', etc.")
84 | )]
85 | UnknownType(SourceId, #[label("This type is unknown")] SourceSpan),
86 |
87 | #[error("Uncallable expression")]
88 | #[diagnostic(
89 | code(nak::uncallable_expr),
90 | help("Only classes and functions are callable")
91 | )]
92 | UncallableExpression(SourceId, #[label("'{2}' is uncallable")] SourceSpan, Type),
93 |
94 | #[error("Only instances have properties")]
95 | #[diagnostic(code(nak::only_instances_have_properties))]
96 | OnlyInstancesAndClassesHaveProperties(
97 | SourceId,
98 | #[label("Expected instance type, but got {2} instead")] SourceSpan,
99 | Type,
100 | ),
101 |
102 | #[error("Function returns incompatible type")]
103 | #[diagnostic(code(nak::incompatible_types))]
104 | FunctionHasIncompatibleReturnType(
105 | SourceId,
106 | #[label("Expected a type that is compatible with {2}")] SourceSpan,
107 | Type,
108 | #[label("Found type {4} instead")] SourceSpan,
109 | Type,
110 | ),
111 |
112 | #[error("Can't return from global scope")]
113 | #[diagnostic(
114 | code(nak::cannot_return_from_global_scope),
115 | help("Can only return from within function scopes")
116 | )]
117 | CantReturnFromGlobalScope(
118 | SourceId,
119 | #[label("Cannot return from this scope")] SourceSpan,
120 | ),
121 |
122 | #[error("List shorthand count must be of type int")]
123 | #[diagnostic(code(nak::list_shorthand_count_must_be_int))]
124 | ListShorthandCountMustBeInt(
125 | SourceId,
126 | #[label("This must be compatible with int")] SourceSpan,
127 | ),
128 |
129 | #[error("Cannot redeclare symbol {1}")]
130 | #[diagnostic(code(nak::cannot_redeclare_symbol))]
131 | CannotRedeclareSymbol(
132 | SourceId,
133 | String,
134 | #[label("This identifier")] SourceSpan,
135 | #[label("has been previously defined here")] SourceSpan
136 | )
137 | }
138 |
--------------------------------------------------------------------------------
/crates/interpreter/src/lib.rs:
--------------------------------------------------------------------------------
1 | pub mod env;
2 | pub mod error;
3 | mod expr;
4 | mod value;
5 |
6 | use crate::env::{Environment, ScopeId};
7 | use crate::error::RuntimeError;
8 | use crate::expr::eval_expr;
9 | use ast::stmt::*;
10 | use meta::trace;
11 | use parser::Parse;
12 |
13 | pub use crate::value::{Builtin, Val, Value};
14 |
15 | pub fn interpret(parse: Parse, env: &mut Environment) -> miette::Result<()> {
16 | for _stmt in parse.stmts {
17 | eval_stmt(_stmt, env, 0)?;
18 | }
19 |
20 | trace!(format!("{:?}", env));
21 |
22 | Ok(())
23 | }
24 |
25 | fn eval_stmt(stmt: Statement, env: &mut Environment, scope: ScopeId) -> Result<(), RuntimeError> {
26 | match stmt.stmt {
27 | Stmt::Expr(expr) => {
28 | eval_expr(expr, env, scope)?;
29 | }
30 | Stmt::Variable { .. } => {
31 | eval_variable(stmt, env, scope)?;
32 | }
33 | Stmt::Block(..) => {
34 | eval_block(stmt, env, scope)?;
35 | }
36 | Stmt::Return(expr) => {
37 | let expr = expr.map_or(Ok(Value::null()), |expr| eval_expr(expr, env, scope))?;
38 | return Err(RuntimeError::EarlyReturn(expr));
39 | }
40 | Stmt::Function(..) => eval_func_decl(stmt, env, scope)?,
41 | Stmt::Class(..) => eval_class_decl(stmt, env, scope)?,
42 | Stmt::If { .. } => eval_if(stmt, env, scope)?,
43 | Stmt::Until { .. } => eval_until(stmt, env, scope)?,
44 | }
45 |
46 | Ok(())
47 | }
48 |
49 | fn eval_variable(
50 | stmt: Statement,
51 | env: &mut Environment,
52 | scope: ScopeId,
53 | ) -> Result<(), RuntimeError> {
54 | if let Stmt::Variable {
55 | name: binding,
56 | expr,
57 | } = stmt.stmt
58 | {
59 | let var_name = binding.name.item;
60 |
61 | let mut val = Value::null();
62 |
63 | if let Some(expr) = expr {
64 | val = eval_expr(expr, env, scope)?;
65 | }
66 |
67 | env.define(scope, var_name, val)?;
68 |
69 | Ok(())
70 | } else {
71 | panic!("ICE: eval_variable should only be called with Stmt::Variable");
72 | }
73 | }
74 |
75 | fn eval_block(stmt: Statement, env: &mut Environment, scope: ScopeId) -> Result<(), RuntimeError> {
76 | if let Stmt::Block(stmts) = stmt.stmt {
77 | for _stmt in stmts {
78 | eval_stmt(_stmt, env, scope)?;
79 | }
80 |
81 | Ok(())
82 | } else {
83 | panic!("ICE: eval_block should only be called with Stmt::Block");
84 | }
85 | }
86 |
87 | fn eval_func_decl(
88 | stmt: Statement,
89 | env: &mut Environment,
90 | scope: ScopeId,
91 | ) -> Result<(), RuntimeError> {
92 | if let Stmt::Function(func) = &stmt.stmt {
93 | let func_name = func.name.item.clone();
94 | env.define(scope, func_name, Value::from_function(stmt, scope))?;
95 |
96 | Ok(())
97 | } else {
98 | panic!("ICE: eval_func should only be called with Stmt::Function");
99 | }
100 | }
101 |
102 | fn eval_class_decl(
103 | stmt: Statement,
104 | env: &mut Environment,
105 | scope: ScopeId,
106 | ) -> Result<(), RuntimeError> {
107 | if let Stmt::Class(class) = &stmt.stmt {
108 | let class_name = class.name.clone();
109 |
110 | // make sure we don't define anything that collides with the class name
111 | env.define(scope, class_name.item.clone(), Value::null())?;
112 |
113 | let new_scope = env.begin_scope(scope);
114 |
115 | // We have to define it manually, but we will bind 'this' on the instance
116 | env.define(new_scope, String::from("this"), Value::null())?;
117 |
118 | let val = Value::from_class(stmt, env, new_scope)?;
119 |
120 | env.assign(scope, class_name, val)?;
121 |
122 | Ok(())
123 | } else {
124 | panic!("ICE: eval_class_decl should only be called with Stmt::Class");
125 | }
126 | }
127 |
128 | fn eval_if(stmt: Statement, env: &mut Environment, scope: ScopeId) -> Result<(), RuntimeError> {
129 | if let Stmt::If {
130 | cond,
131 | body,
132 | else_branch,
133 | } = stmt.stmt
134 | {
135 | let cond = eval_expr(cond, env, scope)?;
136 |
137 | let new_scope = env.begin_scope(scope);
138 |
139 | if cond.as_bool()? {
140 | eval_block(*body, env, new_scope)?;
141 | } else if let Some(else_branch) = else_branch {
142 | eval_stmt(*else_branch, env, new_scope)?;
143 | }
144 |
145 | env.delete_scope(new_scope);
146 |
147 | Ok(())
148 | } else {
149 | panic!("ICE: eval_if should only be called with Stmt::If");
150 | }
151 | }
152 |
153 | fn eval_until(stmt: Statement, env: &mut Environment, scope: ScopeId) -> Result<(), RuntimeError> {
154 | if let Stmt::Until { cond, body } = stmt.stmt {
155 | loop {
156 | let new_scope = env.begin_scope(scope);
157 | let cond = eval_expr(cond.clone(), env, new_scope)?;
158 | if cond.as_bool()? {
159 | break;
160 | } else {
161 | eval_stmt(*body.clone(), env, new_scope)?;
162 | }
163 |
164 | env.delete_scope(new_scope);
165 | }
166 |
167 | Ok(())
168 | } else {
169 | panic!("ICE: eval_until should only be called with Stmt::Until");
170 | }
171 | }
172 |
--------------------------------------------------------------------------------
/crates/interpreter/src/value/val.rs:
--------------------------------------------------------------------------------
1 | use std::cmp::Ordering;
2 |
3 | use crate::env::Environment;
4 |
5 | use super::{builtin::Builtin, class::Class, Function, InstanceId, ListId};
6 |
7 | #[derive(Debug, Clone)]
8 | pub enum Val {
9 | Bool(bool),
10 | Int(i64),
11 | Float(f64),
12 | String(String),
13 | List { id: ListId },
14 | Function(Function),
15 | Builtin(Builtin),
16 | Class(Class),
17 | Instance { id: InstanceId, name: String },
18 | Null,
19 | }
20 |
21 | impl PartialOrd for Val {
22 | fn partial_cmp(&self, other: &Self) -> Option {
23 | match (self, other) {
24 | (Val::Bool(lhs), rhs) => match rhs {
25 | Val::Bool(rhs) => lhs.partial_cmp(rhs),
26 | Val::Int(..) => Some(Ordering::Less),
27 | Val::Float(..) => Some(Ordering::Less),
28 | Val::String(..) => Some(Ordering::Less),
29 | Val::List { .. } => Some(Ordering::Less),
30 | Val::Function(..) => Some(Ordering::Less),
31 | Val::Builtin(..) => Some(Ordering::Less),
32 | Val::Class(..) => Some(Ordering::Less),
33 | Val::Instance { .. } => Some(Ordering::Less),
34 | Val::Null => Some(Ordering::Less),
35 | },
36 | (Val::Int(lhs), rhs) => match rhs {
37 | Val::Bool(..) => Some(Ordering::Greater),
38 | Val::Int(rhs) => lhs.partial_cmp(rhs),
39 | Val::Float(..) => Some(Ordering::Less),
40 | Val::String(..) => Some(Ordering::Less),
41 | Val::List { .. } => Some(Ordering::Less),
42 | Val::Function(..) => Some(Ordering::Less),
43 | Val::Builtin(..) => Some(Ordering::Less),
44 | Val::Class(..) => Some(Ordering::Less),
45 | Val::Instance { .. } => Some(Ordering::Less),
46 | Val::Null => Some(Ordering::Less),
47 | },
48 | // float,
49 | (Val::String(lhs), rhs) => match rhs {
50 | Val::Bool(..) => Some(Ordering::Greater),
51 | Val::Int(..) => Some(Ordering::Greater),
52 | Val::Float(..) => Some(Ordering::Greater),
53 | Val::String(rhs) => lhs.partial_cmp(rhs),
54 | Val::List { .. } => Some(Ordering::Less),
55 | Val::Function(..) => Some(Ordering::Less),
56 | Val::Builtin(..) => Some(Ordering::Less),
57 | Val::Class(..) => Some(Ordering::Less),
58 | Val::Instance { .. } => Some(Ordering::Less),
59 | Val::Null => Some(Ordering::Less),
60 | },
61 | // others
62 | (Val::Instance { id: lhs, .. }, rhs) => match rhs {
63 | Val::Bool(..) => Some(Ordering::Greater),
64 | Val::Int(..) => Some(Ordering::Greater),
65 | Val::Float(..) => Some(Ordering::Greater),
66 | Val::String(..) => Some(Ordering::Greater),
67 | Val::List { .. } => Some(Ordering::Greater),
68 | Val::Function(..) => Some(Ordering::Greater),
69 | Val::Builtin(..) => Some(Ordering::Less),
70 | Val::Class(..) => Some(Ordering::Greater),
71 | Val::Instance { id: rhs, .. } => lhs.partial_cmp(rhs),
72 | Val::Null => Some(Ordering::Less),
73 | },
74 | (Val::Null, rhs) => match rhs {
75 | Val::Bool(..) => Some(Ordering::Greater),
76 | Val::Int(..) => Some(Ordering::Greater),
77 | Val::Float(..) => Some(Ordering::Greater),
78 | Val::String(..) => Some(Ordering::Greater),
79 | Val::List { .. } => Some(Ordering::Greater),
80 | Val::Function(..) => Some(Ordering::Greater),
81 | Val::Builtin(..) => Some(Ordering::Less),
82 | Val::Class(..) => Some(Ordering::Greater),
83 | Val::Instance { .. } => Some(Ordering::Greater),
84 | Val::Null => Some(Ordering::Equal),
85 | },
86 | _ => todo!("PartialOrd for {:?} and {:?}", self, other),
87 | }
88 | }
89 | }
90 |
91 | impl PartialEq for Val {
92 | fn eq(&self, other: &Self) -> bool {
93 | print!("{:?}", self.partial_cmp(other));
94 | self.partial_cmp(other).map_or(false, Ordering::is_eq)
95 | }
96 | }
97 |
98 | impl std::fmt::Display for Val {
99 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100 | let msg = match self {
101 | Self::Bool(v) => v.to_string(),
102 | Self::Int(v) => v.to_string(),
103 | Self::Float(v) => v.to_string(),
104 | Self::String(v) => v.clone(),
105 | Self::List { id } => format!("list (id {})", id),
106 | Self::Null => String::from("null"),
107 | Self::Function(func) => {
108 | format!("{} (closure {})", func.func.name.item.clone(), func.closure)
109 | }
110 | Self::Builtin(builtin) => format!("{}", builtin),
111 | Self::Class(class) => class.class.name.item.to_string(),
112 | Self::Instance { id, name } => format!("{} instance (id {})", name.clone(), id),
113 | };
114 |
115 | f.write_str(msg.as_str())
116 | }
117 | }
118 |
119 | impl Val {
120 | pub fn to_string(&self, env: &mut Environment) -> String {
121 | match self {
122 | Self::Bool(v) => v.to_string(),
123 | Self::Int(v) => v.to_string(),
124 | Self::Float(v) => v.to_string(),
125 | Self::String(v) => v.clone(),
126 | Self::List { id } => {
127 | let mut cloned_env = env.clone();
128 | let list = cloned_env.get_list(*id);
129 | list.to_string(env)
130 | }
131 | Self::Null => String::from("null"),
132 | Self::Function(func) => {
133 | format!("{} (closure {})", func.func.name.item.clone(), func.closure)
134 | }
135 | Self::Builtin(builtin) => format!("{}", builtin),
136 | Self::Class(class) => class.class.name.item.to_string(),
137 | Self::Instance { id, name } => format!("{} instance (id {})", name.clone(), id),
138 | }
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/crates/ast/src/ty.rs:
--------------------------------------------------------------------------------
1 | use meta::Span;
2 |
3 | use crate::{
4 | expr::Expression,
5 | op::{Op, Operator},
6 | stmt::Binding,
7 | };
8 |
9 | #[derive(Clone, Debug, PartialEq, Eq)]
10 | pub enum Type {
11 | Int,
12 | Float,
13 | Bool,
14 | String,
15 | List(Box),
16 | Class(String),
17 | Instance(String),
18 | Function {
19 | params: Vec,
20 | returns: Box,
21 | },
22 | Null,
23 | Any,
24 | }
25 |
26 | #[derive(Clone, Debug, PartialEq, Eq)]
27 | pub struct TypeExpression {
28 | pub ty: Type,
29 | pub span: Span,
30 | }
31 |
32 | impl TypeExpression {
33 | pub fn any() -> Self {
34 | Self {
35 | ty: Type::Any,
36 | span: Span::garbage(),
37 | }
38 | }
39 |
40 | pub fn string() -> Self {
41 | Self {
42 | ty: Type::String,
43 | span: Span::garbage(),
44 | }
45 | }
46 | }
47 |
48 | impl From for TypeExpression {
49 | fn from(binding: Binding) -> Self {
50 | Self {
51 | span: binding.name.span,
52 | ty: binding.ty,
53 | }
54 | }
55 | }
56 |
57 | impl std::fmt::Display for Type {
58 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59 | let msg: String = match self {
60 | Self::Int => String::from("int"),
61 | Self::Float => String::from("float"),
62 | Self::Bool => String::from("bool"),
63 | Self::String => String::from("string"),
64 | Self::List(ty) => format!("[{}]", ty.ty),
65 | Self::Class(name) => name.clone(),
66 | Self::Instance(name) => format!("instanceof {name}"),
67 | Self::Function { params, returns } => format!(
68 | "({}) -> {}",
69 | params
70 | .iter()
71 | .map(|p| format!("{}", p.ty))
72 | .collect::>()
73 | .join(", "),
74 | returns.ty
75 | ),
76 | Self::Null => String::from("null"),
77 | Self::Any => String::from("any"),
78 | };
79 |
80 | f.write_str(&msg)
81 | }
82 | }
83 |
84 | pub fn type_compatible(lhs: &Type, rhs: &Type) -> bool {
85 | match (lhs, rhs) {
86 | (Type::Int, Type::Float) => true,
87 | (Type::Float, Type::Int) => true,
88 | (Type::Null, _) => true,
89 | (Type::List(lhs), Type::List(rhs)) => type_compatible(&lhs.ty, &rhs.ty),
90 | (
91 | Type::Function {
92 | params: lhs_params,
93 | returns: lhs_returns,
94 | },
95 | Type::Function {
96 | params: rhs_params,
97 | returns: rhs_returns,
98 | },
99 | ) => {
100 | if lhs_params.len() != rhs_params.len() {
101 | return false;
102 | }
103 |
104 | // params
105 | for (lhs, rhs) in lhs_params.iter().zip(rhs_params.iter()) {
106 | if !type_compatible(&lhs.ty, &rhs.ty) {
107 | return false;
108 | }
109 | }
110 |
111 | // return type
112 | if !type_compatible(&lhs_returns.ty, &rhs_returns.ty) {
113 | return false;
114 | }
115 |
116 | true
117 | }
118 |
119 | (Type::Any, _) => true,
120 | (_, Type::Any) => true,
121 |
122 | (lhs, rhs) => lhs == rhs,
123 | }
124 | }
125 |
126 | pub fn result_type(lhs: &Expression, op: &Operator, rhs: &Expression) -> Option {
127 | match op.op {
128 | Op::Add => match (&lhs.ty, &rhs.ty) {
129 | (Type::Int, Type::Int) => Some(Type::Int),
130 | (Type::Int, Type::Float) => Some(Type::Float),
131 | (Type::Float, Type::Int) => Some(Type::Float),
132 | (Type::Float, Type::Float) => Some(Type::Float),
133 | (Type::String, Type::String) => Some(Type::String),
134 | (Type::String, Type::Int) => Some(Type::String),
135 | (Type::Int, Type::String) => Some(Type::String),
136 | (Type::List(lhs), Type::List(rhs)) => {
137 | if type_compatible(&lhs.ty, &rhs.ty) {
138 | Some(Type::List(lhs.clone()))
139 | } else {
140 | None
141 | }
142 | }
143 |
144 | (Type::Null, _) => None,
145 | (_, Type::Null) => None,
146 |
147 | (Type::Any, _) => Some(Type::Any),
148 | (_, Type::Any) => Some(Type::Any),
149 |
150 | _ => None,
151 | },
152 | Op::Sub => match (&lhs.ty, &rhs.ty) {
153 | (Type::Int, Type::Int) => Some(Type::Int),
154 | (Type::Int, Type::Float) => Some(Type::Float),
155 | (Type::Float, Type::Int) => Some(Type::Float),
156 | (Type::Float, Type::Float) => Some(Type::Float),
157 |
158 | (Type::Null, _) => None,
159 | (_, Type::Null) => None,
160 |
161 | (Type::Any, _) => Some(Type::Any),
162 | (_, Type::Any) => Some(Type::Any),
163 |
164 | _ => None,
165 | },
166 | Op::Mul => match (&lhs.ty, &rhs.ty) {
167 | (Type::Int, Type::Int) => Some(Type::Int),
168 | (Type::Int, Type::Float) => Some(Type::Float),
169 | (Type::Float, Type::Int) => Some(Type::Float),
170 | (Type::Float, Type::Float) => Some(Type::Float),
171 |
172 | (Type::Null, _) => None,
173 | (_, Type::Null) => None,
174 |
175 | (Type::Any, _) => Some(Type::Any),
176 | (_, Type::Any) => Some(Type::Any),
177 |
178 | _ => None,
179 | },
180 | Op::Div => match (&lhs.ty, &rhs.ty) {
181 | (Type::Int, Type::Int) => Some(Type::Int),
182 | (Type::Int, Type::Float) => Some(Type::Float),
183 | (Type::Float, Type::Int) => Some(Type::Float),
184 | (Type::Float, Type::Float) => Some(Type::Float),
185 |
186 | (Type::Null, _) => None,
187 | (_, Type::Null) => None,
188 |
189 | (Type::Any, _) => Some(Type::Any),
190 | (_, Type::Any) => Some(Type::Any),
191 |
192 | _ => None,
193 | },
194 | Op::And | Op::Or => match (&lhs.ty, &rhs.ty) {
195 | (Type::Bool, Type::Bool) => Some(Type::Bool),
196 |
197 | (Type::Null, _) => None,
198 | (_, Type::Null) => None,
199 |
200 | (Type::Any, _) => Some(Type::Any),
201 | (_, Type::Any) => Some(Type::Any),
202 |
203 | _ => None,
204 | },
205 | Op::LessThan | Op::LessThanEquals | Op::GreaterThan | Op::GreaterThanEquals => {
206 | match (&lhs.ty, &rhs.ty) {
207 | (Type::Int, Type::Int) => Some(Type::Int),
208 | (Type::Int, Type::Float) => Some(Type::Float),
209 | (Type::Float, Type::Int) => Some(Type::Float),
210 | (Type::Float, Type::Float) => Some(Type::Float),
211 |
212 | (Type::Null, _) => None,
213 | (_, Type::Null) => None,
214 |
215 | (Type::Any, _) => Some(Type::Any),
216 | (_, Type::Any) => Some(Type::Any),
217 |
218 | _ => None,
219 | }
220 | }
221 | Op::Equals | Op::NotEquals => Some(Type::Bool),
222 | _ => None,
223 | }
224 | }
225 |
--------------------------------------------------------------------------------
/crates/nakala/src/main.rs:
--------------------------------------------------------------------------------
1 | use ast::ty::{Type, TypeExpression};
2 | use compiler::compile;
3 | use interpreter::{env::Environment, error::RuntimeError, interpret, Builtin, Val, Value};
4 | use meta::Span;
5 | use miette::Result;
6 | use parser::{parse, source::Source, SymbolTable};
7 | use reedline::{DefaultPrompt, Reedline, Signal};
8 | use std::{fs::read_to_string, path::Path};
9 |
10 | fn main() -> Result<()> {
11 | let args = parse_arguments();
12 |
13 | let builtins = get_builtins();
14 | let symbols = builtins
15 | .clone()
16 | .into_iter()
17 | .map(|b| b.as_symbol())
18 | .collect();
19 |
20 | let mut env = Environment::new(builtins)?;
21 | let symtab = SymbolTable::new(symbols);
22 |
23 | if args.use_interpreter && args.input_files.is_empty() {
24 | repl(args)
25 | } else {
26 | if args.input_files.len() > 1 {
27 | todo!("multiple files are not supported yet.");
28 | }
29 |
30 | for source in args.input_files.into_iter() {
31 | let parse = parse(source.clone(), symtab.clone())
32 | .map_err(|error| error.with_source_code(source.clone()))?;
33 |
34 | if args.show_parse {
35 | println!("{:#?}", parse);
36 | }
37 |
38 | if args.use_compiler {
39 | let res = compile(parse).map_err(|error| error.with_source_code(source))?;
40 | println!("{}", res);
41 | } else {
42 | interpret(parse, &mut env).map_err(|error| error.with_source_code(source))?;
43 | }
44 | }
45 |
46 | Ok(())
47 | }
48 | }
49 |
50 | fn repl(args: NakArguments) -> Result<()> {
51 | let mut line_editor = Reedline::create();
52 | let prompt = DefaultPrompt::default();
53 |
54 | let builtins = get_builtins();
55 | let symbols = builtins
56 | .clone()
57 | .into_iter()
58 | .map(|b| b.as_symbol())
59 | .collect();
60 |
61 | let mut env = Environment::new(builtins)?;
62 | let mut symtab = SymbolTable::new(symbols);
63 |
64 | loop {
65 | let sig = line_editor.read_line(&prompt).unwrap();
66 | match sig {
67 | Signal::Success(buffer) => {
68 | let source = Source::new(0, buffer, "stdin".to_string());
69 |
70 | let parse = parse(source.clone(), symtab)
71 | .map_err(|error| error.with_source_code(source.clone()))?;
72 |
73 | if args.show_parse {
74 | println!("{:#?}", parse);
75 | }
76 |
77 | symtab = parse.symtab.clone();
78 |
79 | interpret(parse, &mut env).map_err(|error| error.with_source_code(source))?;
80 | }
81 | Signal::CtrlD | Signal::CtrlC => {
82 | println!("\nAborted!");
83 | break Ok(());
84 | }
85 | }
86 | }
87 | }
88 |
89 | fn get_builtins() -> Vec {
90 | let mut builtins = vec![];
91 |
92 | // print
93 | fn print(vals: Vec, env: &mut Environment) -> Result {
94 | println!(
95 | "{}",
96 | vals.first()
97 | .expect("arity mismatch didn't catch builtin")
98 | .to_string(env)
99 | );
100 |
101 | Ok(Value::null())
102 | }
103 | builtins.push(Builtin::new(
104 | String::from("print"),
105 | vec![Type::Any],
106 | None,
107 | print,
108 | ));
109 |
110 | // exit
111 | fn exit(vals: Vec, _: &mut Environment) -> Result {
112 | let code = vals
113 | .first()
114 | .expect("arity mismatch didn't catch builtin")
115 | .as_int()
116 | .expect("builtin didn't catch type mismatch");
117 |
118 | if let Ok(code) = i32::try_from(code) {
119 | std::process::exit(code);
120 | } else {
121 | panic!("Exit code can't be bigger than i32");
122 | }
123 | }
124 | builtins.push(Builtin::new(
125 | String::from("exit"),
126 | vec![Type::Int],
127 | None,
128 | exit,
129 | ));
130 |
131 | // str
132 | fn str(vals: Vec, env: &mut Environment) -> Result {
133 | let val = vals.first().expect("arity mismatch didn't catch builtin");
134 | Ok(Value {
135 | val: Val::String(val.to_string(env)),
136 | span: val.span,
137 | ty: Type::String,
138 | })
139 | }
140 | builtins.push(Builtin::new(
141 | String::from("str"),
142 | vec![Type::String],
143 | Some(Type::String),
144 | str,
145 | ));
146 |
147 | // type
148 | fn type_(vals: Vec, _: &mut Environment) -> Result {
149 | let val = vals.first().expect("arity mismatch didn't catch builtin");
150 | Ok(Value {
151 | val: Val::String(format!("{}", val.ty)),
152 | ty: Type::String,
153 | span: val.span,
154 | })
155 | }
156 | builtins.push(Builtin::new(
157 | String::from("type"),
158 | vec![Type::Any],
159 | Some(Type::String),
160 | type_,
161 | ));
162 |
163 | //len
164 | fn len(vals: Vec, env: &mut Environment) -> Result {
165 | let val = vals.first().expect("arity mismatch didn't catch builtin");
166 | match val.val {
167 | Val::List { id } => Ok(env.get_list(id).len()),
168 | _ => todo!(""),
169 | }
170 | }
171 | builtins.push(Builtin::new(
172 | String::from("len"),
173 | vec![Type::Any],
174 | Some(Type::Int),
175 | len,
176 | ));
177 |
178 | // chars
179 | fn chars(vals: Vec, env: &mut Environment) -> Result {
180 | let val = vals.first().expect("arity mismatch didn't catch builtin");
181 | match &val.val {
182 | Val::String(s) => {
183 | let new_list = env.new_list(
184 | s.chars()
185 | .map(|c| Value {
186 | span: Span::garbage(),
187 | ty: Type::String,
188 | val: Val::String(String::from(c)),
189 | })
190 | .collect(),
191 | Type::String,
192 | );
193 | Ok(new_list)
194 | }
195 | _ => unreachable!("ICE: buliltin typechecking failed"),
196 | }
197 | }
198 |
199 | builtins.push(Builtin::new(
200 | String::from("chars"),
201 | vec![Type::String],
202 | Some(Type::List(Box::new(TypeExpression::string()))),
203 | chars,
204 | ));
205 |
206 | builtins
207 | }
208 |
209 | #[derive(Debug)]
210 | struct NakArguments {
211 | input_files: Vec,
212 | use_interpreter: bool,
213 | use_compiler: bool,
214 | show_parse: bool,
215 | }
216 |
217 | fn parse_arguments() -> NakArguments {
218 | let args: Vec = std::env::args().collect();
219 |
220 | let is_present = |flags: &[&str]| args.iter().any(|arg| flags.contains(&arg.as_str()));
221 |
222 | let show_parse = is_present(&["-p", "--show-parse"]);
223 | let use_compiler = is_present(&["-c", "--compile"]);
224 |
225 | let mut next_file_id = 0;
226 | let input_files = args
227 | .into_iter()
228 | .filter(|arg| arg.ends_with(".nak"))
229 | .filter_map(|filepath| {
230 | let path = Path::new(&filepath);
231 | if path.exists() {
232 | if let Ok(contents) = read_to_string(path) {
233 | let t = Source::new(next_file_id, contents, filepath);
234 | next_file_id += 1;
235 | return Some(t);
236 | }
237 | }
238 |
239 | None
240 | })
241 | .collect();
242 |
243 | NakArguments {
244 | input_files,
245 | use_interpreter: !use_compiler,
246 | use_compiler,
247 | show_parse,
248 | }
249 | }
250 |
--------------------------------------------------------------------------------
/crates/interpreter/src/env.rs:
--------------------------------------------------------------------------------
1 | use ast::ty::Type;
2 | use meta::{trace, Span, Spanned};
3 |
4 | use crate::{
5 | error::RuntimeError,
6 | value::{self, Builtin, Instance, InstanceId, List, Val, Value},
7 | };
8 | use std::collections::{hash_map::Entry, HashMap};
9 |
10 | pub type ScopeId = usize;
11 | pub type ListId = usize;
12 |
13 | #[derive(Clone, PartialEq, Default)]
14 | pub struct Environment {
15 | pub scopes: Vec,
16 | next_scope_id: ScopeId,
17 | instances: Vec,
18 | next_instance_id: InstanceId,
19 | lists: Vec,
20 | next_list_id: ListId,
21 | }
22 |
23 | impl Environment {
24 | pub fn new(builtins: Vec) -> Result {
25 | let mut env = Self {
26 | scopes: vec![Scope::new(0, None)],
27 | next_scope_id: 1,
28 | instances: vec![],
29 | next_instance_id: 0,
30 | lists: vec![],
31 | next_list_id: 0,
32 | };
33 |
34 | env.define_builtins(builtins)?;
35 |
36 | Ok(env)
37 | }
38 |
39 | fn define_builtins(&mut self, builtins: Vec) -> Result<(), RuntimeError> {
40 | for builtin in builtins {
41 | self.define(
42 | 0,
43 | builtin.name.clone(),
44 | Value {
45 | ty: builtin.ty.clone(),
46 | val: Val::Builtin(builtin),
47 | span: Span::garbage(),
48 | },
49 | )?;
50 | }
51 |
52 | Ok(())
53 | }
54 |
55 | pub fn new_instance(&mut self, class: value::Class, span: Span) -> Value {
56 | let id = self.next_instance_id;
57 | self.next_instance_id += 1;
58 |
59 | let name = class.class.name.item.clone();
60 |
61 | self.instances.push(Instance::new(id, class));
62 |
63 | Value {
64 | ty: Type::Instance(name.clone()),
65 | val: Val::Instance { id, name },
66 | span,
67 | }
68 | }
69 |
70 | pub fn get_instance(&mut self, instance_id: InstanceId) -> Result<&mut Instance, RuntimeError> {
71 | Ok(self
72 | .instances
73 | .get_mut(instance_id)
74 | .expect("ICE: Called get instance on instance that doesn't exist"))
75 | }
76 |
77 | pub fn new_list(&mut self, vals: Vec, ty: Type) -> Value {
78 | let id = self.next_list_id;
79 | self.next_list_id += 1;
80 |
81 | self.lists.push(List::new(id, vals));
82 |
83 | Value {
84 | ty,
85 | span: Span::garbage(),
86 | val: Val::List { id },
87 | }
88 | }
89 |
90 | pub fn get_list(&mut self, list_id: ListId) -> &mut List {
91 | self.lists
92 | .get_mut(list_id)
93 | .expect("ICE: Called get_list on list that doesn't exist")
94 | }
95 |
96 | pub fn begin_scope(&mut self, enclosing: ScopeId) -> ScopeId {
97 | let id = self.next_scope_id;
98 | self.next_scope_id += 1;
99 |
100 | self.scopes.push(Scope::new(id, Some(enclosing)));
101 |
102 | trace!(format!("created scope {:?}", self.scopes.last().unwrap()));
103 |
104 | id
105 | }
106 |
107 | pub fn delete_scope(&mut self, _scope_id: ScopeId) {
108 | // TODO - handle cleaning up non closure scopes that we don't need to stick around
109 | // for example, after evaluating an if statement block
110 |
111 | //todo!("delete scope")
112 | }
113 |
114 | pub fn get(&self, scope_id: ScopeId, name: &Spanned) -> Result {
115 | let scope = self
116 | .scopes
117 | .get(scope_id)
118 | .expect("ICE: no matching Scope for ScopeId");
119 | trace!(format!(
120 | "trying to get {} in scope {:#?}",
121 | name.item, &scope
122 | ));
123 |
124 | match scope.get(name) {
125 | Ok(v) => Ok(v),
126 | Err(e) => {
127 | trace!(format!(
128 | "didnt find - checking enclosing scope {:?}",
129 | scope.enclosing
130 | ));
131 | if let Some(enclosing_id) = scope.enclosing {
132 | self.get(enclosing_id, name)
133 | } else {
134 | Err(e)
135 | }
136 | }
137 | }
138 | }
139 |
140 | pub fn assign(
141 | &mut self,
142 | scope_id: ScopeId,
143 | name: Spanned,
144 | val: Value,
145 | ) -> Result<(), RuntimeError> {
146 | let scope = self
147 | .scopes
148 | .get_mut(scope_id)
149 | .expect("ICE: no matching Scope for ScopeId");
150 |
151 | match scope.assign(name.clone(), val.clone()) {
152 | Err(e) => {
153 | if let Some(enclosing_id) = scope.enclosing {
154 | self.assign(enclosing_id, name, val)
155 | } else {
156 | Err(e)
157 | }
158 | }
159 | _ => Ok(()),
160 | }
161 | }
162 |
163 | pub fn define(
164 | &mut self,
165 | scope_id: ScopeId,
166 | name: String,
167 | val: Value,
168 | ) -> Result<(), RuntimeError> {
169 | let scope = self
170 | .scopes
171 | .get_mut(scope_id)
172 | .expect("ICE: no matching Scope for ScopeId");
173 |
174 | match scope.define(name.clone(), val.clone()) {
175 | Err(e) => {
176 | if let Some(enclosing_id) = scope.enclosing {
177 | self.define(enclosing_id, name, val)
178 | } else {
179 | Err(e)
180 | }
181 | }
182 | _ => Ok(()),
183 | }
184 | }
185 | }
186 |
187 | impl std::fmt::Debug for Environment {
188 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
189 | writeln!(f, "Environment has {} Scopes", self.scopes.len())?;
190 |
191 | for scope in self.scopes.iter() {
192 | writeln!(f, "{:?}", scope)?;
193 | }
194 |
195 | Ok(())
196 | }
197 | }
198 |
199 | #[derive(Clone, PartialEq)]
200 | pub struct Scope {
201 | pub id: ScopeId,
202 | values: HashMap,
203 | pub enclosing: Option,
204 | }
205 |
206 | impl std::fmt::Debug for Scope {
207 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
208 | writeln!(f, "Scope {}", self.id)?;
209 | writeln!(f, "\t- Enclosing: {:?}", self.enclosing)?;
210 | writeln!(f, "\t- Values:")?;
211 | for (key, value) in self.values.iter() {
212 | writeln!(f, "\t\t- {} = {}", key, value.ty)?;
213 | }
214 |
215 | Ok(())
216 | }
217 | }
218 |
219 | impl Scope {
220 | pub fn new(id: ScopeId, enclosing: Option) -> Self {
221 | Self {
222 | id,
223 | values: HashMap::default(),
224 | enclosing,
225 | }
226 | }
227 |
228 | pub fn get(&self, name: &Spanned) -> Result {
229 | if let Some(entry) = self.values.get(&name.item) {
230 | return Ok(entry.clone());
231 | }
232 |
233 | Err(RuntimeError::UndefinedVariable(
234 | name.span.source_id,
235 | name.span.into(),
236 | ))
237 | }
238 |
239 | pub fn assign(&mut self, name: Spanned, val: Value) -> Result<(), RuntimeError> {
240 | if let Entry::Occupied(mut e) = self.values.entry(name.item) {
241 | e.insert(val);
242 | Ok(())
243 | } else {
244 | Err(RuntimeError::UndefinedVariable(
245 | name.span.source_id,
246 | name.span.into(),
247 | ))
248 | }
249 | }
250 |
251 | pub fn define(&mut self, name: String, val: Value) -> Result<(), RuntimeError> {
252 | if let Entry::Vacant(e) = self.values.entry(name) {
253 | e.insert(val);
254 | Ok(())
255 | } else {
256 | todo!("can't define var that already exists");
257 | }
258 | }
259 | }
260 |
--------------------------------------------------------------------------------
/crates/interpreter/src/expr.rs:
--------------------------------------------------------------------------------
1 | use crate::{
2 | env::{Environment, ScopeId},
3 | error::RuntimeError,
4 | value::{Callable, Indexible, Val, Value},
5 | };
6 | use ast::{expr::*, op::Op};
7 | use meta::{Span, Spanned};
8 |
9 | pub(crate) fn eval_expr(
10 | expr: Expression,
11 | env: &mut Environment,
12 | scope: ScopeId,
13 | ) -> Result {
14 | match expr.expr {
15 | Expr::Bool(..) | Expr::Int(..) | Expr::Float(..) | Expr::String(..) | Expr::Null => {
16 | Ok(expr.into())
17 | }
18 | Expr::Variable(..) => eval_variable_expr(expr, env, scope),
19 | Expr::Assign { .. } => eval_assign_expr(expr, env, scope),
20 | Expr::Call { .. } => eval_call_expr(expr, env, scope),
21 | Expr::Binary { .. } => eval_binary_expr(expr, env, scope),
22 | Expr::Logical { .. } => eval_logical_expr(expr, env, scope),
23 | Expr::Get { .. } => eval_get_expr(expr, env, scope),
24 | Expr::Set { .. } => eval_set_expr(expr, env, scope),
25 | Expr::This => eval_this_expr(expr, env, scope),
26 | Expr::List(..) => eval_list_expr(expr, env, scope),
27 | Expr::IndexGet { .. } => eval_index_get_expr(expr, env, scope),
28 | Expr::IndexSet { .. } => eval_index_set_expr(expr, env, scope),
29 | Expr::ListShorthand { .. } => eval_list_shorthand_expr(expr, env, scope),
30 | _ => todo!("{:#?} nyi", expr),
31 | }
32 | }
33 |
34 | fn eval_variable_expr(
35 | expr: Expression,
36 | env: &mut Environment,
37 | scope: ScopeId,
38 | ) -> Result {
39 | if let Expr::Variable(name) = expr.expr {
40 | env.get(
41 | scope,
42 | &Spanned {
43 | item: name,
44 | span: expr.span,
45 | },
46 | )
47 | } else {
48 | panic!("ICE: eval_variable_expr should only be called with Expr::Variable");
49 | }
50 | }
51 |
52 | fn eval_assign_expr(
53 | expr: Expression,
54 | env: &mut Environment,
55 | scope: ScopeId,
56 | ) -> Result {
57 | if let Expr::Assign { name, rhs } = expr.expr {
58 | let val = eval_expr(*rhs, env, scope)?;
59 |
60 | env.assign(scope, name, val)?;
61 |
62 | Ok(Value::null())
63 | } else {
64 | panic!("ICE: eval_assign_expr should only be called with Expr::Assign");
65 | }
66 | }
67 |
68 | fn eval_call_expr(
69 | expr: Expression,
70 | env: &mut Environment,
71 | scope: ScopeId,
72 | ) -> Result {
73 | if let Expr::Call { callee, args, .. } = expr.expr {
74 | let callee_span = callee.span;
75 | let val = eval_expr(*callee, env, scope)?;
76 |
77 | match val.val {
78 | Val::Function(func) => func.call(callee_span, args, env, scope),
79 | Val::Class(class) => class.call(callee_span, args, env, scope),
80 | Val::Builtin(builtin) => builtin.call(callee_span, args, env, scope),
81 | _ => panic!("ICE: can only call t"),
82 | }
83 | } else {
84 | panic!("ICE: eval_call expr should only be called with Expr::Call");
85 | }
86 | }
87 |
88 | fn eval_binary_expr(
89 | expr: Expression,
90 | env: &mut Environment,
91 | scope: ScopeId,
92 | ) -> Result {
93 | if let Expr::Binary { lhs, op, rhs } = expr.expr {
94 | let lhs = eval_expr(*lhs, env, scope)?;
95 | let rhs = eval_expr(*rhs, env, scope)?;
96 |
97 | match op.op {
98 | Op::Add => lhs.add(env, op, &rhs),
99 | Op::Sub => lhs.sub(op, &rhs),
100 | Op::Mul => lhs.mul(op, &rhs),
101 | Op::Div => lhs.div(op, &rhs),
102 | Op::Equals => lhs.eq(&rhs),
103 | Op::NotEquals => lhs.neq(&rhs),
104 | Op::LessThanEquals => lhs.lte(op, &rhs),
105 | Op::GreaterThan => lhs.gt(op, &rhs),
106 | Op::GreaterThanEquals => lhs.gte(op, &rhs),
107 | Op::Or | Op::And => {
108 | unreachable!("ICE: logical binary expressions should be parsed as such")
109 | }
110 | _ => todo!("unsupported operation {:#?}", op),
111 | }
112 | } else {
113 | panic!("ICE: eval_binary_expr should only be called with Expr::Binary");
114 | }
115 | }
116 |
117 | fn eval_get_expr(
118 | expr: Expression,
119 | env: &mut Environment,
120 | scope: ScopeId,
121 | ) -> Result {
122 | if let Expr::Get { object, name } = expr.expr {
123 | let obj = eval_expr(*object, env, scope)?;
124 | if let Val::Instance { .. } = obj.val {
125 | let instance = env.get_instance(obj.as_instance()?)?;
126 |
127 | // If property is a function, bind 'this'
128 | let mut prop = instance.get_property(&name.item)?;
129 | if let Val::Function(..) = prop.val {
130 | prop.bind_this(env, obj)?;
131 | }
132 |
133 | Ok(prop)
134 | } else {
135 | let class = obj.as_class()?;
136 | if let Some(entry) = class.statics.get(&name.item) {
137 | Ok(entry.clone())
138 | } else {
139 | Err(RuntimeError::UndefinedClassProperty(
140 | class.class.name.span.source_id,
141 | class.class.name.span.into(),
142 | name.item.to_string(),
143 | ))
144 | }
145 | }
146 | } else {
147 | panic!("ICE: eval_get_expr should only be called with Expr::Get");
148 | }
149 | }
150 |
151 | fn eval_set_expr(
152 | expr: Expression,
153 | env: &mut Environment,
154 | scope: ScopeId,
155 | ) -> Result {
156 | if let Expr::Set { object, name, rhs } = expr.expr {
157 | let obj = eval_expr(*object, env, scope)?;
158 | let val = eval_expr(*rhs, env, scope)?;
159 |
160 | let instance = env.get_instance(obj.as_instance()?)?;
161 | instance.set_property(name.item, val)
162 | } else {
163 | panic!("ICE: eval_set_expr should only be called with Expr::Set");
164 | }
165 | }
166 |
167 | fn eval_logical_expr(
168 | expr: Expression,
169 | env: &mut Environment,
170 | scope: ScopeId,
171 | ) -> Result {
172 | if let Expr::Logical { lhs, op, rhs } = expr.expr {
173 | let span = Span::combine(&[lhs.span, rhs.span]);
174 |
175 | let lhs = eval_expr(*lhs, env, scope)?;
176 |
177 | match op.op {
178 | Op::And => {
179 | // Short circuit if false
180 | if !lhs.as_bool()? {
181 | return Ok((false, span).into());
182 | }
183 |
184 | let rhs = eval_expr(*rhs, env, scope)?;
185 | lhs.and(op, &rhs)
186 | }
187 | Op::Or => {
188 | // Short circuit if true
189 | if lhs.as_bool()? {
190 | return Ok((true, span).into());
191 | }
192 |
193 | let rhs = eval_expr(*rhs, env, scope)?;
194 | lhs.or(op, &rhs)
195 | }
196 | _ => unreachable!(
197 | "ICE: logical expressions was given non logical operator {:?}",
198 | op.op
199 | ),
200 | }
201 | } else {
202 | panic!("ICE: eval_logical_expr should only be called with Expr::Logical");
203 | }
204 | }
205 |
206 | fn eval_this_expr(
207 | expr: Expression,
208 | env: &mut Environment,
209 | scope: ScopeId,
210 | ) -> Result {
211 | if let Expr::This = expr.expr {
212 | let t: Spanned = Spanned {
213 | item: String::from("this"),
214 | span: expr.span,
215 | };
216 |
217 | env.get(scope, &t)
218 | } else {
219 | panic!("ICE: eval_this_expr should only be called with Expr::This");
220 | }
221 | }
222 |
223 | fn eval_list_expr(
224 | expr: Expression,
225 | env: &mut Environment,
226 | scope: ScopeId,
227 | ) -> Result {
228 | if let Expr::List(list) = expr.expr {
229 | let mut vals = vec![];
230 | for val in list.into_iter() {
231 | vals.push(eval_expr(val, env, scope)?);
232 | }
233 |
234 | Ok(env.new_list(vals, expr.ty.clone()))
235 | } else {
236 | panic!("ICE: eval_list_expr should only be called with Expr::List");
237 | }
238 | }
239 |
240 | fn eval_index_get_expr(
241 | expr: Expression,
242 | env: &mut Environment,
243 | scope: ScopeId,
244 | ) -> Result {
245 | if let Expr::IndexGet { lhs, index } = expr.expr {
246 | let lhs = eval_expr(*lhs, env, scope)?;
247 | let index = eval_expr(*index, env, scope)?;
248 |
249 | match lhs.val {
250 | Val::List { id } => env.get_list(id).get(index.as_int()?.try_into().unwrap()),
251 | _ => panic!("ICE: can only index Lists"),
252 | }
253 | } else {
254 | panic!("ICE: eval_index_get_expr should only be called with Expr::Index");
255 | }
256 | }
257 |
258 | fn eval_index_set_expr(
259 | expr: Expression,
260 | env: &mut Environment,
261 | scope: ScopeId,
262 | ) -> Result {
263 | if let Expr::IndexSet { lhs, index, rhs } = expr.expr {
264 | let lhs = eval_expr(*lhs, env, scope)?;
265 | let index = eval_expr(*index, env, scope)?;
266 | let rhs = eval_expr(*rhs, env, scope)?;
267 |
268 | let lhs = match lhs.val {
269 | Val::List { id } => env.get_list(id),
270 | _ => panic!("ICE: can only index Lists"),
271 | };
272 |
273 | lhs.set(index.as_int()?.try_into().unwrap(), rhs)?;
274 |
275 | Ok(Value::null())
276 | } else {
277 | panic!("ICE: eval_index_set_expr should only be called with Expr::IndexSet");
278 | }
279 | }
280 |
281 | fn eval_list_shorthand_expr(
282 | expr: Expression,
283 | env: &mut Environment,
284 | scope: ScopeId,
285 | ) -> Result {
286 | if let Expr::ListShorthand { value, count } = expr.expr {
287 | let value = eval_expr(*value, env, scope)?;
288 | let count = eval_expr(*count, env, scope)?.as_int()?;
289 |
290 | let mut vals: Vec = Vec::with_capacity(count.try_into().unwrap());
291 | for _ in 0..count {
292 | vals.push(value.clone());
293 | }
294 |
295 | Ok(env.new_list(vals, value.ty))
296 | } else {
297 | panic!("ICE: eval_list_shorthand_expr should only be called with Expr::ListShorthand");
298 | }
299 | }
300 |
--------------------------------------------------------------------------------
/crates/lexer/src/token_kind.rs:
--------------------------------------------------------------------------------
1 | use logos::Logos;
2 | use std::fmt;
3 |
4 | #[derive(Logos, Debug, Copy, Clone, PartialEq)]
5 | pub enum TokenKind {
6 | #[regex(r"[\s\t\n\f]+")]
7 | Whitespace,
8 | #[regex(r"//.*")]
9 | Comment,
10 |
11 | // Single-character tokens
12 | #[token("(")]
13 | LeftParen,
14 | #[token(")")]
15 | RightParen,
16 | #[token("[")]
17 | LeftBracket,
18 | #[token("]")]
19 | RightBracket,
20 | #[token("{")]
21 | LeftBrace,
22 | #[token("}")]
23 | RightBrace,
24 | #[token(",")]
25 | Comma,
26 | #[token(".")]
27 | Dot,
28 | #[token("-")]
29 | Minus,
30 | #[token("+")]
31 | Plus,
32 | #[token(";")]
33 | Semicolon,
34 | #[token(":")]
35 | Colon,
36 | #[token("/")]
37 | Slash,
38 | #[token("*")]
39 | Star,
40 |
41 | // One or more character tokens
42 | #[token("!")]
43 | Bang,
44 | #[token("!=")]
45 | BangEqual,
46 | #[token("=")]
47 | Equal,
48 | #[token("==")]
49 | EqualEqual,
50 | #[token(">")]
51 | Greater,
52 | #[token(">=")]
53 | GreaterEqual,
54 | #[token("<")]
55 | Less,
56 | #[token("<=")]
57 | LessEqual,
58 | #[token("->")]
59 | Arrow,
60 |
61 | // Literals
62 | #[regex("[A-Za-z_][A-Za-z0-9_]*")]
63 | Ident,
64 | #[regex(r#""[^"]*""#)]
65 | String,
66 | #[regex("[0-9]+")]
67 | Int,
68 | #[regex(r#"[0-9]+\.[0-9]+"#)]
69 | Float,
70 |
71 | // Keywords
72 | #[token("and")]
73 | And,
74 | #[token("class")]
75 | Class,
76 | #[token("else")]
77 | Else,
78 | #[token("false")]
79 | False,
80 | #[token("func")]
81 | Func,
82 | #[token("if")]
83 | If,
84 | #[token("null")]
85 | Null,
86 | #[token("or")]
87 | Or,
88 | #[token("ret")]
89 | Ret,
90 | #[token("this")]
91 | This,
92 | #[token("true")]
93 | True,
94 | #[token("let")]
95 | Let,
96 | #[token("until")]
97 | Until,
98 | #[token("static")]
99 | Static,
100 | #[token("enum")]
101 | Enum,
102 |
103 | // Types
104 | #[token("int")]
105 | TypeInt,
106 | #[token("float")]
107 | TypeFloat,
108 | #[token("bool")]
109 | TypeBool,
110 | #[token("string")]
111 | TypeString,
112 | #[token("any")]
113 | TypeAny,
114 |
115 | #[error]
116 | Error,
117 | }
118 |
119 | impl TokenKind {
120 | pub fn is_trivia(self) -> bool {
121 | matches!(self, Self::Whitespace | Self::Comment)
122 | }
123 | }
124 |
125 | impl fmt::Display for TokenKind {
126 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127 | f.write_str(match self {
128 | Self::Whitespace => "whitespace",
129 | Self::Comment => "comment",
130 |
131 | // Single-character tokens
132 | Self::LeftParen => "(",
133 | Self::RightParen => ")",
134 | Self::LeftBracket => "[",
135 | Self::RightBracket => "]",
136 | Self::LeftBrace => "{",
137 | Self::RightBrace => "}",
138 | Self::Comma => ",",
139 | Self::Dot => ".",
140 | Self::Minus => "-",
141 | Self::Plus => "+",
142 | Self::Semicolon => ";",
143 | Self::Colon => ":",
144 | Self::Slash => "/",
145 | Self::Star => "*",
146 |
147 | // One or more character tokens
148 | Self::Bang => "!",
149 | Self::BangEqual => "!=",
150 | Self::Equal => "=",
151 | Self::EqualEqual => "==",
152 | Self::Greater => ">",
153 | Self::GreaterEqual => ">=",
154 | Self::Less => "<",
155 | Self::LessEqual => "<=",
156 | Self::Arrow => "->",
157 |
158 | // Literals
159 | Self::Ident => "ident",
160 | Self::String => "string",
161 | Self::Int => "int",
162 | Self::Float => "float",
163 |
164 | // Keywords
165 | Self::And => "and",
166 | Self::Class => "class",
167 | Self::Else => "else",
168 | Self::False => "false",
169 | Self::Func => "func",
170 | Self::If => "if",
171 | Self::Null => "null",
172 | Self::Or => "or",
173 | Self::Ret => "ret",
174 | Self::This => "this",
175 | Self::True => "true",
176 | Self::Let => "let",
177 | Self::Until => "until",
178 | Self::Static => "static",
179 | Self::Enum => "enum",
180 |
181 | // Types
182 | Self::TypeInt => "int",
183 | Self::TypeFloat => "float",
184 | Self::TypeBool => "bool",
185 | Self::TypeString => "string",
186 | Self::TypeAny => "any",
187 |
188 | Self::Error => "error",
189 | })
190 | }
191 | }
192 |
193 | #[cfg(test)]
194 | mod tests {
195 | use super::*;
196 | use crate::Lexer;
197 |
198 | fn check(input: &str, kind: TokenKind) {
199 | let mut lexer = Lexer::new(0, input);
200 |
201 | let token = lexer.next().unwrap();
202 | assert_eq!(token.kind, kind);
203 | assert_eq!(token.text, input);
204 | assert_eq!(lexer.next(), None);
205 | }
206 |
207 | #[test]
208 | fn lex_spaces_and_newlines() {
209 | check(" \n", TokenKind::Whitespace);
210 | }
211 |
212 | #[test]
213 | fn lex_tabs_and_spaces() {
214 | check("\t ", TokenKind::Whitespace);
215 | check(" \t", TokenKind::Whitespace);
216 | }
217 |
218 | #[test]
219 | fn lex_comment() {
220 | check("// this is a comment", TokenKind::Comment);
221 | }
222 |
223 | #[test]
224 | fn lex_comment_excluding_next_line() {
225 | let mut lexer = Lexer::new(
226 | 0,
227 | r"//this is a comment
228 | +",
229 | );
230 |
231 | let token = lexer.next().unwrap();
232 | assert_eq!(token.kind, TokenKind::Comment);
233 | assert_eq!(token.text, "//this is a comment");
234 | }
235 |
236 | #[test]
237 | fn lex_left_paren() {
238 | check("(", TokenKind::LeftParen);
239 | }
240 |
241 | #[test]
242 | fn lex_right_paren() {
243 | check(")", TokenKind::RightParen);
244 | }
245 |
246 | #[test]
247 | fn lex_left_bracket() {
248 | check("[", TokenKind::LeftBracket);
249 | }
250 |
251 | #[test]
252 | fn lex_right_bracket() {
253 | check("]", TokenKind::RightBracket);
254 | }
255 |
256 | #[test]
257 | fn lex_left_brace() {
258 | check("{", TokenKind::LeftBrace);
259 | }
260 |
261 | #[test]
262 | fn lex_right_brace() {
263 | check("}", TokenKind::RightBrace);
264 | }
265 |
266 | #[test]
267 | fn lex_comma() {
268 | check(",", TokenKind::Comma);
269 | }
270 |
271 | #[test]
272 | fn lex_dot() {
273 | check(".", TokenKind::Dot);
274 | }
275 |
276 | #[test]
277 | fn lex_minus() {
278 | check("-", TokenKind::Minus);
279 | }
280 |
281 | #[test]
282 | fn lex_plus() {
283 | check("+", TokenKind::Plus);
284 | }
285 |
286 | #[test]
287 | fn lex_semicolon() {
288 | check(";", TokenKind::Semicolon);
289 | }
290 |
291 | #[test]
292 | fn lex_colon() {
293 | check(":", TokenKind::Colon);
294 | }
295 |
296 | #[test]
297 | fn lex_slash() {
298 | check("/", TokenKind::Slash);
299 | }
300 |
301 | #[test]
302 | fn lex_star() {
303 | check("*", TokenKind::Star);
304 | }
305 |
306 | #[test]
307 | fn lex_bang() {
308 | check("!", TokenKind::Bang);
309 | }
310 |
311 | #[test]
312 | fn lex_bang_equal() {
313 | check("!=", TokenKind::BangEqual);
314 | }
315 |
316 | #[test]
317 | fn lex_equal() {
318 | check("=", TokenKind::Equal);
319 | }
320 |
321 | #[test]
322 | fn lex_equal_equal() {
323 | check("==", TokenKind::EqualEqual);
324 | }
325 |
326 | #[test]
327 | fn lex_greater() {
328 | check(">", TokenKind::Greater);
329 | }
330 |
331 | #[test]
332 | fn lex_greater_equal() {
333 | check(">=", TokenKind::GreaterEqual);
334 | }
335 |
336 | #[test]
337 | fn lex_less() {
338 | check("<", TokenKind::Less);
339 | }
340 |
341 | #[test]
342 | fn lex_less_equal() {
343 | check("<=", TokenKind::LessEqual);
344 | }
345 |
346 | #[test]
347 | fn lex_simple_ident() {
348 | check("foo123", TokenKind::Ident);
349 | }
350 |
351 | #[test]
352 | fn lex_weird_ident() {
353 | check("A_91238i291_sdfa", TokenKind::Ident);
354 | }
355 |
356 | #[test]
357 | fn lex_leading_underscore_ident() {
358 | check("_foo", TokenKind::Ident);
359 | }
360 |
361 | #[test]
362 | fn lex_simple_string() {
363 | check(r#""foobar""#, TokenKind::String);
364 | }
365 |
366 | #[test]
367 | fn lex_weird_string() {
368 | check(
369 | r#""jfsdkaljf asdk kfjsd akfjsda asd fiasd""#,
370 | TokenKind::String,
371 | );
372 | }
373 |
374 | #[test]
375 | fn lex_integer() {
376 | check("1", TokenKind::Int);
377 | }
378 |
379 | #[test]
380 | fn lex_float() {
381 | check("123.4", TokenKind::Float);
382 | }
383 |
384 | #[test]
385 | fn lex_zero_leading_float() {
386 | check("0.123", TokenKind::Float);
387 | }
388 |
389 | #[test]
390 | fn lex_and() {
391 | check("and", TokenKind::And);
392 | }
393 |
394 | #[test]
395 | fn lex_class() {
396 | check("class", TokenKind::Class);
397 | }
398 |
399 | #[test]
400 | fn lex_else() {
401 | check("else", TokenKind::Else);
402 | }
403 |
404 | #[test]
405 | fn lex_false() {
406 | check("false", TokenKind::False);
407 | }
408 |
409 | #[test]
410 | fn lex_func() {
411 | check("func", TokenKind::Func);
412 | }
413 |
414 | #[test]
415 | fn lex_if() {
416 | check("if", TokenKind::If);
417 | }
418 |
419 | #[test]
420 | fn lex_null() {
421 | check("null", TokenKind::Null);
422 | }
423 |
424 | #[test]
425 | fn lex_or() {
426 | check("or", TokenKind::Or);
427 | }
428 |
429 | #[test]
430 | fn lex_ret() {
431 | check("ret", TokenKind::Ret);
432 | }
433 |
434 | #[test]
435 | fn lex_this() {
436 | check("this", TokenKind::This);
437 | }
438 |
439 | #[test]
440 | fn lex_true() {
441 | check("true", TokenKind::True);
442 | }
443 |
444 | #[test]
445 | fn lex_let() {
446 | check("let", TokenKind::Let);
447 | }
448 |
449 | #[test]
450 | fn lex_until() {
451 | check("until", TokenKind::Until);
452 | }
453 |
454 | #[test]
455 | fn lex_type_int() {
456 | check("int", TokenKind::TypeInt);
457 | }
458 |
459 | #[test]
460 | fn lex_type_float() {
461 | check("float", TokenKind::TypeFloat);
462 | }
463 |
464 | #[test]
465 | fn lex_type_bool() {
466 | check("bool", TokenKind::TypeBool);
467 | }
468 |
469 | #[test]
470 | fn lex_type_string() {
471 | check("string", TokenKind::TypeString);
472 | }
473 |
474 | #[test]
475 | fn lex_type_any() {
476 | check("any", TokenKind::TypeAny);
477 | }
478 |
479 | #[test]
480 | fn lex_arrow() {
481 | check("->", TokenKind::Arrow);
482 | }
483 |
484 | #[test]
485 | fn lex_static() {
486 | check("static", TokenKind::Static);
487 | }
488 |
489 | #[test]
490 | fn lex_enum() {
491 | check("enum", TokenKind::Enum);
492 | }
493 | }
494 |
--------------------------------------------------------------------------------
/crates/interpreter/src/value/mod.rs:
--------------------------------------------------------------------------------
1 | mod builtin;
2 | mod class;
3 | mod function;
4 | mod instance;
5 | mod list;
6 | mod val;
7 |
8 | use std::{cmp::Ordering, collections::HashMap};
9 |
10 | use ast::{
11 | expr::{Expr, Expression},
12 | op::Operator,
13 | stmt::{Statement, Stmt},
14 | ty::{Type, TypeExpression},
15 | };
16 | pub use builtin::*;
17 | pub use class::*;
18 | pub use function::*;
19 | pub use instance::*;
20 | pub use list::*;
21 | use meta::Span;
22 | pub use val::*;
23 |
24 | use crate::{
25 | env::{Environment, ScopeId},
26 | error::RuntimeError,
27 | expr::eval_expr,
28 | };
29 |
30 | pub trait Callable {
31 | fn arity(&self) -> usize;
32 | fn call(
33 | &self,
34 | callee_span: Span,
35 | args: Vec,
36 | env: &mut Environment,
37 | scope: ScopeId,
38 | ) -> Result;
39 | }
40 |
41 | pub trait Indexible {
42 | fn get(&self, index: usize) -> Result;
43 | fn set(&mut self, index: usize, val: Value) -> Result<(), RuntimeError>;
44 | }
45 |
46 | #[derive(Debug, Clone, PartialEq)]
47 | pub struct Value {
48 | pub val: Val,
49 | pub span: Span,
50 | pub ty: Type,
51 | }
52 |
53 | impl From for Value {
54 | fn from(expr: Expression) -> Self {
55 | let val = match expr.expr {
56 | Expr::Bool(v) => Val::Bool(v),
57 | Expr::Int(v) => Val::Int(v),
58 | Expr::Float(v) => Val::Float(v),
59 | Expr::String(v) => Val::String(v),
60 | Expr::Null => Val::Null,
61 | _ => {
62 | panic!(
63 | "ICE: attempted to turn non simple expression {:?} into a Value",
64 | expr
65 | );
66 | }
67 | };
68 |
69 | Self {
70 | val,
71 | span: expr.span,
72 | ty: expr.ty,
73 | }
74 | }
75 | }
76 |
77 | impl From<(bool, Span)> for Value {
78 | fn from(pair: (bool, Span)) -> Self {
79 | let val = Val::Bool(pair.0);
80 | let span = pair.1;
81 |
82 | Self {
83 | val,
84 | span,
85 | ty: Type::Bool,
86 | }
87 | }
88 | }
89 |
90 | impl Value {
91 | pub fn null() -> Self {
92 | Self {
93 | val: Val::Null,
94 | span: Span::garbage(),
95 | ty: Type::Null,
96 | }
97 | }
98 |
99 | pub fn from_function(stmt: Statement, closure: ScopeId) -> Self {
100 | if let Stmt::Function(func) = stmt.stmt {
101 | Value {
102 | ty: func.ty.ty.clone(),
103 | val: Val::Function(Function { func, closure }),
104 | span: stmt.span,
105 | }
106 | } else {
107 | panic!("ICE: from_function should only be called with Stmt::Function")
108 | }
109 | }
110 |
111 | pub fn from_class(
112 | stmt: Statement,
113 | env: &mut Environment,
114 | scope: ScopeId,
115 | ) -> Result {
116 | if let Stmt::Class(class) = stmt.stmt {
117 | let mut methods: HashMap = HashMap::default();
118 | let mut statics: HashMap = HashMap::default();
119 |
120 | for method_stmt in class.methods.clone() {
121 | if let Stmt::Function(func) = &method_stmt.stmt {
122 | let name = func.name.item.clone();
123 | let runtime_function = Value::from_function(method_stmt, scope);
124 |
125 | methods.insert(name, runtime_function);
126 | } else {
127 | panic!("ICE: class methods must be Stmt::Function");
128 | }
129 | }
130 |
131 | for static_stmt in class.statics.clone() {
132 | if let Stmt::Variable { name, expr } = static_stmt.stmt {
133 | let name = name.name.item.clone();
134 |
135 | let mut val = Value::null();
136 |
137 | if let Some(expr) = expr {
138 | val = eval_expr(expr, env, scope)?;
139 | }
140 |
141 | statics.insert(name, val);
142 | } else {
143 | panic!("ICE: class statics must be Stmt::Variable");
144 | }
145 | }
146 |
147 | Ok(Value {
148 | ty: Type::Class(class.name.item.clone()),
149 | val: Val::Class(Class {
150 | class,
151 | methods,
152 | statics,
153 | }),
154 | span: stmt.span,
155 | })
156 | } else {
157 | panic!("ICE: from_class should onyl be called with Stmt::Class");
158 | }
159 | }
160 |
161 | pub fn add(
162 | &self,
163 | env: &mut Environment,
164 | op: Operator,
165 | rhs: &Value,
166 | ) -> Result {
167 | let span = Span::combine(&[self.span, rhs.span]);
168 |
169 | match (&self.val, &rhs.val) {
170 | (Val::Int(lhs), Val::Int(rhs)) => Ok(Value {
171 | val: Val::Int(lhs + rhs),
172 | span,
173 | ty: Type::Int,
174 | }),
175 | (Val::String(lhs), Val::String(rhs)) => Ok(Value {
176 | val: Val::String(format!("{}{}", lhs, rhs)),
177 | span,
178 | ty: Type::String,
179 | }),
180 | (Val::Int(lhs), Val::String(rhs)) => Ok(Value {
181 | val: Val::String(format!("{}{}", lhs, rhs)),
182 | span,
183 | ty: Type::String,
184 | }),
185 | (Val::String(lhs), Val::Int(rhs)) => Ok(Value {
186 | val: Val::String(format!("{}{}", lhs, rhs)),
187 | span,
188 | ty: Type::String,
189 | }),
190 | (Val::List { id: lhs_id }, Val::List { id: rhs_id }) => {
191 | // TODO shouldn't have to clone entire env every time
192 | let mut cloned_env = env.clone();
193 |
194 | let lhs = env.get_list(*lhs_id);
195 | let rhs = cloned_env.get_list(*rhs_id);
196 |
197 | lhs.extend_with(rhs.clone());
198 | Ok(self.clone())
199 | }
200 | _ => Err(RuntimeError::UnsupportedOperation(
201 | self.span.source_id,
202 | op.span.into(),
203 | self.span.into(),
204 | self.ty.clone(),
205 | rhs.span.into(),
206 | rhs.ty.clone(),
207 | )),
208 | }
209 | }
210 |
211 | pub fn sub(&self, op: Operator, rhs: &Value) -> Result {
212 | let span = Span::combine(&[self.span, rhs.span]);
213 |
214 | match (&self.val, &rhs.val) {
215 | (Val::Int(lhs), Val::Int(rhs)) => Ok(Value {
216 | val: Val::Int(lhs - rhs),
217 | span,
218 | ty: Type::Int,
219 | }),
220 | (Val::Int(lhs), Val::Float(rhs)) => Ok(Value {
221 | val: Val::Float(*lhs as f64 - *rhs),
222 | span,
223 | ty: Type::Float,
224 | }),
225 | (Val::Float(lhs), Val::Int(rhs)) => Ok(Value {
226 | val: Val::Float(*lhs - *rhs as f64),
227 | span,
228 | ty: Type::Float,
229 | }),
230 | _ => Err(RuntimeError::UnsupportedOperation(
231 | self.span.source_id,
232 | op.span.into(),
233 | self.span.into(),
234 | self.ty.clone(),
235 | rhs.span.into(),
236 | rhs.ty.clone(),
237 | )),
238 | }
239 | }
240 |
241 | pub fn mul(&self, op: Operator, rhs: &Value) -> Result {
242 | let span = Span::combine(&[self.span, rhs.span]);
243 |
244 | match (&self.val, &rhs.val) {
245 | (Val::Int(lhs), Val::Int(rhs)) => Ok(Value {
246 | val: Val::Int(lhs * rhs),
247 | span,
248 | ty: Type::Int,
249 | }),
250 | (Val::Int(lhs), Val::Float(rhs)) => Ok(Value {
251 | val: Val::Float(*lhs as f64 * *rhs),
252 | span,
253 | ty: Type::Float,
254 | }),
255 | (Val::Float(lhs), Val::Int(rhs)) => Ok(Value {
256 | val: Val::Float(*lhs * *rhs as f64),
257 | span,
258 | ty: Type::Float,
259 | }),
260 | _ => Err(RuntimeError::UnsupportedOperation(
261 | self.span.source_id,
262 | op.span.into(),
263 | self.span.into(),
264 | self.ty.clone(),
265 | rhs.span.into(),
266 | rhs.ty.clone(),
267 | )),
268 | }
269 | }
270 |
271 | pub fn div(&self, op: Operator, rhs: &Value) -> Result {
272 | let span = Span::combine(&[self.span, rhs.span]);
273 |
274 | match (&self.val, &rhs.val) {
275 | (Val::Int(lhs), Val::Int(rhs)) => {
276 | if *rhs != 0 {
277 | if lhs % rhs == 0 {
278 | Ok(Value {
279 | val: Val::Int(lhs / rhs),
280 | span,
281 | ty: Type::Int,
282 | })
283 | } else {
284 | Ok(Value {
285 | val: Val::Float((*lhs as f64) / (*rhs as f64)),
286 | span,
287 | ty: Type::Float,
288 | })
289 | }
290 | } else {
291 | todo!("divide by 0")
292 | }
293 | }
294 | _ => Err(RuntimeError::UnsupportedOperation(
295 | self.span.source_id,
296 | op.span.into(),
297 | self.span.into(),
298 | self.ty.clone(),
299 | rhs.span.into(),
300 | rhs.ty.clone(),
301 | )),
302 | }
303 | }
304 |
305 | pub fn eq(&self, rhs: &Value) -> Result {
306 | let span = Span::combine(&[self.span, rhs.span]);
307 |
308 | let val = matches!(self.val.partial_cmp(&rhs.val), Some(Ordering::Equal));
309 |
310 | Ok(Value {
311 | val: Val::Bool(val),
312 | span,
313 | ty: Type::Bool,
314 | })
315 | }
316 |
317 | pub fn neq(&self, rhs: &Value) -> Result {
318 | let span = Span::combine(&[self.span, rhs.span]);
319 |
320 | let val = match self.val.partial_cmp(&rhs.val) {
321 | Some(ordering) => !matches!(ordering, Ordering::Equal),
322 | None => false,
323 | };
324 |
325 | Ok(Value {
326 | val: Val::Bool(val),
327 | span,
328 | ty: Type::Bool,
329 | })
330 | }
331 |
332 | pub fn lte(&self, op: Operator, rhs: &Value) -> Result {
333 | let span = Span::combine(&[self.span, rhs.span]);
334 |
335 | match (&self.val, &rhs.val) {
336 | (Val::Int(lhs), Val::Int(rhs)) => Ok((lhs <= rhs, span).into()),
337 | _ => Err(RuntimeError::UnsupportedOperation(
338 | self.span.source_id,
339 | op.span.into(),
340 | self.span.into(),
341 | self.ty.clone(),
342 | rhs.span.into(),
343 | rhs.ty.clone(),
344 | )),
345 | }
346 | }
347 |
348 | pub fn gt(&self, op: Operator, rhs: &Value) -> Result {
349 | let span = Span::combine(&[self.span, rhs.span]);
350 |
351 | match (&self.val, &rhs.val) {
352 | (Val::Int(lhs), Val::Int(rhs)) => Ok((lhs > rhs, span).into()),
353 | _ => Err(RuntimeError::UnsupportedOperation(
354 | self.span.source_id,
355 | op.span.into(),
356 | self.span.into(),
357 | self.ty.clone(),
358 | rhs.span.into(),
359 | rhs.ty.clone(),
360 | )),
361 | }
362 | }
363 |
364 | pub fn gte(&self, op: Operator, rhs: &Value) -> Result {
365 | let span = Span::combine(&[self.span, rhs.span]);
366 |
367 | match (&self.val, &rhs.val) {
368 | (Val::Int(lhs), Val::Int(rhs)) => Ok((lhs >= rhs, span).into()),
369 | _ => Err(RuntimeError::UnsupportedOperation(
370 | self.span.source_id,
371 | op.span.into(),
372 | self.span.into(),
373 | self.ty.clone(),
374 | rhs.span.into(),
375 | rhs.ty.clone(),
376 | )),
377 | }
378 | }
379 |
380 | pub fn and(&self, _op: Operator, rhs: &Value) -> Result {
381 | let span = Span::combine(&[self.span, rhs.span]);
382 |
383 | let lhs = self.as_bool()?;
384 | let rhs = rhs.as_bool()?;
385 |
386 | Ok(Value {
387 | val: Val::Bool(lhs && rhs),
388 | span,
389 | ty: Type::Bool,
390 | })
391 | }
392 |
393 | pub fn or(&self, _op: Operator, rhs: &Value) -> Result {
394 | let span = Span::combine(&[self.span, rhs.span]);
395 |
396 | let lhs = self.as_bool()?;
397 | let rhs = rhs.as_bool()?;
398 |
399 | Ok(Value {
400 | val: Val::Bool(lhs || rhs),
401 | span,
402 | ty: Type::Bool,
403 | })
404 | }
405 |
406 | pub fn as_bool(&self) -> Result {
407 | match &self.val {
408 | Val::Bool(v) => Ok(*v),
409 | _ => Err(RuntimeError::UnexpectedValueType(
410 | self.span.source_id,
411 | Type::Bool,
412 | format!("{}", self.val),
413 | self.span.into(),
414 | )),
415 | }
416 | }
417 |
418 | pub fn as_int(&self) -> Result {
419 | match &self.val {
420 | Val::Int(v) => Ok(*v),
421 | _ => Err(RuntimeError::UnexpectedValueType(
422 | self.span.source_id,
423 | Type::Int,
424 | format!("{}", self.val),
425 | self.span.into(),
426 | )),
427 | }
428 | }
429 |
430 | pub fn as_instance(&self) -> Result {
431 | match &self.val {
432 | Val::Instance { id, .. } => Ok(*id),
433 | _ => Err(RuntimeError::UnexpectedValueType(
434 | self.span.source_id,
435 | Type::Instance(String::from("any")),
436 | format!("{}", self.val),
437 | self.span.into(),
438 | )),
439 | }
440 | }
441 |
442 | pub fn as_function(&self) -> Result {
443 | match &self.val {
444 | Val::Function(func) => Ok(func.clone()),
445 | _ => Err(RuntimeError::UnexpectedValueType(
446 | self.span.source_id,
447 | Type::Function {
448 | params: vec![TypeExpression::any()],
449 | returns: Box::new(TypeExpression::any()),
450 | },
451 | format!("{}", self.val),
452 | self.span.into(),
453 | )),
454 | }
455 | }
456 |
457 | pub fn as_class(&self) -> Result {
458 | match &self.val {
459 | Val::Class(class) => Ok(class.clone()),
460 | _ => Err(RuntimeError::UnexpectedValueType(
461 | self.span.source_id,
462 | Type::Class(String::from("unkown")),
463 | format!("{}", self.val),
464 | self.span.into(),
465 | )),
466 | }
467 | }
468 |
469 | pub fn bind_this(
470 | &mut self,
471 | env: &mut Environment,
472 | instance: Value,
473 | ) -> Result<(), RuntimeError> {
474 | if let Val::Function(ref mut func) = self.val {
475 | if let Val::Instance { .. } = instance.val {
476 | let binded_scope = env.begin_scope(func.closure);
477 | env.define(binded_scope, String::from("this"), instance)?;
478 | func.closure = binded_scope;
479 | Ok(())
480 | } else {
481 | panic!("Can only bind instance's on functions");
482 | }
483 | } else {
484 | panic!("Can only use 'bind_this' on Val::Function");
485 | }
486 | }
487 |
488 | pub fn to_string(&self, env: &mut Environment) -> String {
489 | self.val.to_string(env)
490 | }
491 | }
492 |
--------------------------------------------------------------------------------
/crates/parser/src/lib.rs:
--------------------------------------------------------------------------------
1 | pub mod error;
2 | mod parser;
3 | pub mod source;
4 | mod symtab;
5 |
6 | use crate::parser::Parser;
7 | use crate::source::Source;
8 | use ast::stmt::Statement;
9 | pub use symtab::{Sym, Symbol, SymbolTable};
10 |
11 | #[derive(Debug, PartialEq)]
12 | pub struct Parse {
13 | pub stmts: Vec,
14 | pub symtab: SymbolTable,
15 | }
16 |
17 | pub fn parse(source: Source, symtab: SymbolTable) -> miette::Result {
18 | Parser::new(source, symtab).parse()
19 | }
20 |
21 | #[cfg(test)]
22 | mod tests {
23 | use super::*;
24 | use expect_test::{expect, Expect};
25 |
26 | impl<'a> Into for &'a str {
27 | fn into(self) -> Source {
28 | Source::new(0, String::from(self), "".into())
29 | }
30 | }
31 |
32 | fn check(actual: &str, expected: Expect) {
33 | let result = format!(
34 | "{:#?}",
35 | parse(actual.into(), SymbolTable::new(vec![])).unwrap()
36 | );
37 | expected.assert_eq(result.as_str())
38 | }
39 |
40 | #[test]
41 | fn parse_string() {
42 | check(
43 | r#""foo";"#,
44 | expect![[r#"
45 | Parse {
46 | stmts: [
47 | Statement {
48 | stmt: Expr(
49 | Expression {
50 | expr: String(
51 | "foo",
52 | ),
53 | span: Span {
54 | source_id: 0,
55 | start: 0,
56 | end: 5,
57 | },
58 | ty: String,
59 | },
60 | ),
61 | span: Span {
62 | source_id: 0,
63 | start: 0,
64 | end: 6,
65 | },
66 | },
67 | ],
68 | symtab: SymbolTable {
69 | inner: [
70 | {},
71 | ],
72 | },
73 | }"#]],
74 | );
75 | }
76 |
77 | #[test]
78 | fn parse_true() {
79 | check(
80 | "true;",
81 | expect![[r#"
82 | Parse {
83 | stmts: [
84 | Statement {
85 | stmt: Expr(
86 | Expression {
87 | expr: Bool(
88 | true,
89 | ),
90 | span: Span {
91 | source_id: 0,
92 | start: 0,
93 | end: 4,
94 | },
95 | ty: Bool,
96 | },
97 | ),
98 | span: Span {
99 | source_id: 0,
100 | start: 0,
101 | end: 5,
102 | },
103 | },
104 | ],
105 | symtab: SymbolTable {
106 | inner: [
107 | {},
108 | ],
109 | },
110 | }"#]],
111 | );
112 | }
113 |
114 | #[test]
115 | fn parse_false() {
116 | check(
117 | "false;",
118 | expect![[r#"
119 | Parse {
120 | stmts: [
121 | Statement {
122 | stmt: Expr(
123 | Expression {
124 | expr: Bool(
125 | false,
126 | ),
127 | span: Span {
128 | source_id: 0,
129 | start: 0,
130 | end: 5,
131 | },
132 | ty: Bool,
133 | },
134 | ),
135 | span: Span {
136 | source_id: 0,
137 | start: 0,
138 | end: 6,
139 | },
140 | },
141 | ],
142 | symtab: SymbolTable {
143 | inner: [
144 | {},
145 | ],
146 | },
147 | }"#]],
148 | );
149 | }
150 |
151 | #[test]
152 | fn parse_null() {
153 | check(
154 | "null;",
155 | expect![[r#"
156 | Parse {
157 | stmts: [
158 | Statement {
159 | stmt: Expr(
160 | Expression {
161 | expr: Null,
162 | span: Span {
163 | source_id: 0,
164 | start: 0,
165 | end: 4,
166 | },
167 | ty: Null,
168 | },
169 | ),
170 | span: Span {
171 | source_id: 0,
172 | start: 0,
173 | end: 5,
174 | },
175 | },
176 | ],
177 | symtab: SymbolTable {
178 | inner: [
179 | {},
180 | ],
181 | },
182 | }"#]],
183 | );
184 | }
185 |
186 | #[test]
187 | fn parse_integer() {
188 | check(
189 | "5;",
190 | expect![[r#"
191 | Parse {
192 | stmts: [
193 | Statement {
194 | stmt: Expr(
195 | Expression {
196 | expr: Int(
197 | 5,
198 | ),
199 | span: Span {
200 | source_id: 0,
201 | start: 0,
202 | end: 1,
203 | },
204 | ty: Int,
205 | },
206 | ),
207 | span: Span {
208 | source_id: 0,
209 | start: 0,
210 | end: 2,
211 | },
212 | },
213 | ],
214 | symtab: SymbolTable {
215 | inner: [
216 | {},
217 | ],
218 | },
219 | }"#]],
220 | );
221 | }
222 |
223 | #[test]
224 | fn parse_float() {
225 | check(
226 | "1.23;",
227 | expect![[r#"
228 | Parse {
229 | stmts: [
230 | Statement {
231 | stmt: Expr(
232 | Expression {
233 | expr: Float(
234 | 1.23,
235 | ),
236 | span: Span {
237 | source_id: 0,
238 | start: 0,
239 | end: 4,
240 | },
241 | ty: Float,
242 | },
243 | ),
244 | span: Span {
245 | source_id: 0,
246 | start: 0,
247 | end: 5,
248 | },
249 | },
250 | ],
251 | symtab: SymbolTable {
252 | inner: [
253 | {},
254 | ],
255 | },
256 | }"#]],
257 | );
258 | }
259 |
260 | //#[test]
261 | //fn parse_add() {
262 | // check(
263 | // vec![Stmt::Expr(Expr::Binary {
264 | // lhs: Box::new(Expr::Literal(Literal::Number(1.0))),
265 | // op: Op::Add,
266 | // rhs: Box::new(Expr::Literal(Literal::Number(2.0))),
267 | // })],
268 | // "1 + 2;",
269 | // );
270 | //}
271 |
272 | //#[test]
273 | //fn parse_sub() {
274 | // check(
275 | // vec![Stmt::Expr(Expr::Binary {
276 | // lhs: Box::new(Expr::Literal(Literal::Number(1.12))),
277 | // op: Op::Sub,
278 | // rhs: Box::new(Expr::Literal(Literal::Number(2.0))),
279 | // })],
280 | // "1.12 - 2;",
281 | // )
282 | //}
283 |
284 | //#[test]
285 | //fn parse_mul() {
286 | // check(
287 | // vec![Stmt::Expr(Expr::Binary {
288 | // lhs: Box::new(Expr::Literal(Literal::Number(2.0))),
289 | // op: Op::Mul,
290 | // rhs: Box::new(Expr::Literal(Literal::Number(32.31))),
291 | // })],
292 | // "2 * 32.31;",
293 | // );
294 | //}
295 |
296 | //#[test]
297 | //fn parse_div() {
298 | // check(
299 | // vec![Stmt::Expr(Expr::Binary {
300 | // lhs: Box::new(Expr::Literal(Literal::Number(100.5))),
301 | // op: Op::Div,
302 | // rhs: Box::new(Expr::Literal(Literal::Number(0.5))),
303 | // })],
304 | // "100.5 / 0.5;",
305 | // );
306 | //}
307 |
308 | //#[test]
309 | //fn parse_unary() {
310 | // check(
311 | // vec![Stmt::Expr(Expr::Unary {
312 | // op: Op::Sub,
313 | // rhs: Box::new(Expr::Literal(Literal::Number(123.4))),
314 | // })],
315 | // "-123.4;",
316 | // );
317 | //}
318 |
319 | //#[test]
320 | //fn parse_grouping() {
321 | // check(
322 | // vec![Stmt::Expr(Expr::Grouping(Box::new(Expr::Binary {
323 | // lhs: Box::new(Expr::Grouping(Box::new(Expr::Literal(Literal::Number(
324 | // 1.0,
325 | // ))))),
326 | // op: Op::Add,
327 | // rhs: Box::new(Expr::Unary {
328 | // op: Op::Sub,
329 | // rhs: Box::new(Expr::Literal(Literal::Number(42.3))),
330 | // }),
331 | // })))],
332 | // "((1.0) + -42.3);",
333 | // );
334 | //}
335 |
336 | //#[test]
337 | //fn parse_not_equal() {
338 | // check(
339 | // vec![Stmt::Expr(Expr::Binary {
340 | // lhs: Box::new(Expr::Literal(Literal::String("foo".to_string()))),
341 | // op: Op::NotEquals,
342 | // rhs: Box::new(Expr::Literal(Literal::String("bar".to_string()))),
343 | // })],
344 | // r#""foo" != "bar";"#,
345 | // );
346 | //}
347 |
348 | //#[test]
349 | //fn parse_equal() {
350 | // check(
351 | // vec![Stmt::Expr(Expr::Binary {
352 | // lhs: Box::new(Expr::Literal(Literal::Number(42.0))),
353 | // op: Op::Equals,
354 | // rhs: Box::new(Expr::Literal(Literal::Null)),
355 | // })],
356 | // "42.0 == null;",
357 | // );
358 | //}
359 |
360 | //#[test]
361 | //fn parse_less_than() {
362 | // check(
363 | // vec![Stmt::Expr(Expr::Binary {
364 | // lhs: Box::new(Expr::Literal(Literal::Number(10.0))),
365 | // op: Op::LessThan,
366 | // rhs: Box::new(Expr::Literal(Literal::Number(42.0))),
367 | // })],
368 | // "10.0 < 42.0;",
369 | // );
370 | //}
371 |
372 | //#[test]
373 | //fn parse_less_than_equals() {
374 | // check(
375 | // vec![Stmt::Expr(Expr::Binary {
376 | // lhs: Box::new(Expr::Literal(Literal::Number(10.0))),
377 | // op: Op::LessThanEquals,
378 | // rhs: Box::new(Expr::Literal(Literal::Number(10.0))),
379 | // })],
380 | // "10.0 <= 10.0;",
381 | // );
382 | //}
383 |
384 | //#[test]
385 | //fn parse_variable_decl() {
386 | // check(
387 | // vec![Stmt::Variable {
388 | // name: "something_Int3resting".to_string(),
389 | // expr: None,
390 | // }],
391 | // "let something_Int3resting;",
392 | // );
393 | //}
394 |
395 | //#[test]
396 | //fn parse_variable_decl_with_init() {
397 | // check(
398 | // vec![Stmt::Variable {
399 | // name: "x".to_string(),
400 | // expr: Some(Expr::Variable("x".to_string())),
401 | // }],
402 | // "let x = x;",
403 | // );
404 | //}
405 |
406 | //#[test]
407 | //fn parse_variable_assignment() {
408 | // check(
409 | // vec![Stmt::Expr(Expr::Assign {
410 | // name: "x".to_string(),
411 | // rhs: Box::new(Expr::Literal(Literal::String("foobar".to_string()))),
412 | // })],
413 | // r#"x = "foobar";"#,
414 | // );
415 | //}
416 |
417 | //#[test]
418 | //fn parse_variable_expr() {
419 | // check(
420 | // vec![Stmt::Expr(Expr::Binary {
421 | // lhs: Box::new(Expr::Literal(Literal::Number(100.0))),
422 | // op: Op::Mul,
423 | // rhs: Box::new(Expr::Variable("myVariable".to_string())),
424 | // })],
425 | // "100 * myVariable;",
426 | // );
427 | //}
428 |
429 | //#[test]
430 | //fn parse_empty_block() {
431 | // check(vec![Stmt::Block(vec![])], "{}");
432 | //}
433 |
434 | //#[test]
435 | //fn parse_simple_block() {
436 | // check(
437 | // vec![Stmt::Block(vec![
438 | // Stmt::Variable {
439 | // name: "x".to_string(),
440 | // expr: Some(Expr::Literal(Literal::Number(1.0))),
441 | // },
442 | // Stmt::Expr(Expr::Binary {
443 | // lhs: Box::new(Expr::Literal(Literal::Number(1.0))),
444 | // op: Op::Add,
445 | // rhs: Box::new(Expr::Variable("x".to_string())),
446 | // }),
447 | // ])],
448 | // "{let x = 1; 1 + x;}",
449 | // )
450 | //}
451 |
452 | //#[test]
453 | //fn parse_nested_block() {
454 | // check(
455 | // vec![Stmt::Block(vec![
456 | // Stmt::Variable {
457 | // name: "x".to_string(),
458 | // expr: Some(Expr::Literal(Literal::Number(5.0))),
459 | // },
460 | // Stmt::Block(vec![Stmt::Expr(Expr::Binary {
461 | // lhs: Box::new(Expr::Variable("foo".to_string())),
462 | // op: Op::Sub,
463 | // rhs: Box::new(Expr::Variable("bar".to_string())),
464 | // })]),
465 | // ])],
466 | // "{ let x = 5; { foo - bar; } }",
467 | // )
468 | //}
469 |
470 | //#[test]
471 | //fn parse_simple_if() {
472 | // check(
473 | // vec![Stmt::If {
474 | // cond: Expr::Binary {
475 | // lhs: Box::new(Expr::Variable("x".to_string())),
476 | // op: Op::Equals,
477 | // rhs: Box::new(Expr::Literal(Literal::Number(1.0))),
478 | // },
479 | // body: Box::new(Stmt::Block(vec![Stmt::Print(Expr::Literal(
480 | // Literal::String("x is 1".to_string()),
481 | // ))])),
482 | // else_branch: None,
483 | // }],
484 | // r#"if (x == 1) { print "x is 1"; }"#,
485 | // );
486 | //}
487 |
488 | //#[test]
489 | //fn parse_if_with_else() {
490 | // check(
491 | // vec![Stmt::If {
492 | // cond: Expr::Literal(Literal::False),
493 | // body: Box::new(Stmt::Block(vec![Stmt::Print(Expr::Literal(
494 | // Literal::String("was false".to_string()),
495 | // ))])),
496 | // else_branch: Some(Box::new(Stmt::Block(vec![Stmt::Print(Expr::Literal(
497 | // Literal::String("was true".to_string()),
498 | // ))]))),
499 | // }],
500 | // r#"if (false) { print "was false"; } else { print "was true"; }"#,
501 | // );
502 | //}
503 |
504 | //#[test]
505 | //fn parse_logical_and() {
506 | // check(
507 | // vec![Stmt::Expr(Expr::Logical {
508 | // lhs: Box::new(Expr::Literal(Literal::False)),
509 | // op: Op::And,
510 | // rhs: Box::new(Expr::Literal(Literal::True)),
511 | // })],
512 | // "false and true;",
513 | // );
514 | //}
515 |
516 | //#[test]
517 | //fn parse_logical_or() {
518 | // check(
519 | // vec![Stmt::Expr(Expr::Logical {
520 | // lhs: Box::new(Expr::Literal(Literal::True)),
521 | // op: Op::Or,
522 | // rhs: Box::new(Expr::Variable("x".to_string())),
523 | // })],
524 | // "true or x;",
525 | // );
526 | //}
527 |
528 | //#[test]
529 | //fn parse_until() {
530 | // check(
531 | // vec![Stmt::Until {
532 | // cond: Expr::Literal(Literal::True),
533 | // body: Box::new(Stmt::Block(vec![])),
534 | // }],
535 | // "until (true) { }",
536 | // )
537 | //}
538 |
539 | //#[test]
540 | //fn parse_until_with_body() {
541 | // check(
542 | // vec![
543 | // Stmt::Variable {
544 | // name: "x".to_string(),
545 | // expr: Some(Expr::Literal(Literal::Number(0.0))),
546 | // },
547 | // Stmt::Until {
548 | // cond: Expr::Binary {
549 | // lhs: Box::new(Expr::Variable("x".to_string())),
550 | // op: Op::Equals,
551 | // rhs: Box::new(Expr::Literal(Literal::Number(10.0))),
552 | // },
553 | // body: Box::new(Stmt::Block(vec![
554 | // Stmt::Print(Expr::Literal(Literal::String("iter".to_string()))),
555 | // Stmt::Expr(Expr::Assign {
556 | // name: "x".to_string(),
557 | // rhs: Box::new(Expr::Binary {
558 | // lhs: Box::new(Expr::Variable("x".to_string())),
559 | // op: Op::Add,
560 | // rhs: Box::new(Expr::Literal(Literal::Number(1.0))),
561 | // }),
562 | // }),
563 | // ])),
564 | // },
565 | // ],
566 | // r#"let x = 0; until (x == 10) { print "iter"; x = x + 1; }"#,
567 | // );
568 | //}
569 | }
570 |
--------------------------------------------------------------------------------
/crates/parser/src/parser.rs:
--------------------------------------------------------------------------------
1 | use std::collections::HashMap;
2 |
3 | use crate::{
4 | error::ParseError,
5 | source::Source,
6 | symtab::{Sym, Symbol, SymbolTable},
7 | Parse,
8 | };
9 | use ast::{
10 | expr::{Expr, Expression},
11 | op::{Op, Operator},
12 | stmt::{Binding, Class, Function, Statement, Stmt},
13 | ty::{result_type, type_compatible, Type, TypeExpression},
14 | };
15 | use lexer::{Token, TokenKind};
16 | use meta::{trace, Span, Spanned};
17 |
18 | pub struct Parser {
19 | source: Source,
20 | symtab: SymbolTable,
21 | }
22 |
23 | impl Parser {
24 | pub fn new(source: Source, symtab: SymbolTable) -> Self {
25 | Self { source, symtab }
26 | }
27 |
28 | pub fn parse(mut self) -> miette::Result {
29 | Ok(Parse {
30 | stmts: self.program()?,
31 | symtab: self.symtab,
32 | })
33 | }
34 |
35 | fn is_callable(&self, callee: &Expression) -> Result<(), ParseError> {
36 | let err = Err(ParseError::UncallableExpression(
37 | self.source.id,
38 | callee.span.into(),
39 | callee.ty.clone(),
40 | ));
41 |
42 | match &callee.expr {
43 | Expr::Variable(name) => {
44 | if let Some(entry) = self.symtab.lookup(name) {
45 | if matches!(
46 | entry.ty,
47 | Type::Any | Type::Function { .. } | Type::Class(..)
48 | ) {
49 | return Ok(());
50 | }
51 | }
52 |
53 | err
54 | }
55 |
56 | // TODO nested get exprs
57 | Expr::Get { .. } => Ok(()),
58 | _ => err,
59 | }
60 | }
61 |
62 | fn result_type(
63 | &self,
64 | lhs: &Expression,
65 | op: &Operator,
66 | rhs: &Expression,
67 | ) -> Result {
68 | if let Some(ty) = result_type(lhs, op, rhs) {
69 | Ok(ty)
70 | } else {
71 | Err(ParseError::UnsupportedOperation(
72 | self.source.id,
73 | op.span.into(),
74 | lhs.span.into(),
75 | lhs.ty.clone(),
76 | rhs.span.into(),
77 | rhs.ty.clone(),
78 | ))
79 | }
80 | }
81 |
82 | fn bump(&mut self) -> Result<&Token, ParseError> {
83 | let eof = self.source.eof();
84 | let eof_err = Err(ParseError::UnexpectedEof(self.source.id, eof));
85 |
86 | match self.source.next_token() {
87 | Some(t) => Ok(t),
88 | None => eof_err,
89 | }
90 | }
91 |
92 | fn at(&mut self, kind: TokenKind) -> bool {
93 | self.source.peek_kind() == Some(kind)
94 | }
95 |
96 | fn at_set(&mut self, set: &[TokenKind]) -> bool {
97 | self.source.peek_kind().map_or(false, |k| set.contains(&k))
98 | }
99 |
100 | fn expect(&mut self, kind: TokenKind) -> Result<&Token, ParseError> {
101 | let error_source = self.source.id;
102 |
103 | let t = self.bump()?;
104 | if t.kind == kind {
105 | Ok(t)
106 | } else {
107 | let actual = t.text.to_string();
108 | let span = t.span;
109 | Err(ParseError::ExpectedToken(
110 | error_source,
111 | actual,
112 | kind,
113 | span.into(),
114 | ))
115 | }
116 | }
117 |
118 | fn program(&mut self) -> Result, ParseError> {
119 | trace!("parse_program");
120 | let mut stmts: Vec = Vec::new();
121 | while !self.source.at_end() {
122 | stmts.push(self.decl()?);
123 | }
124 |
125 | Ok(stmts)
126 | }
127 |
128 | fn decl(&mut self) -> Result {
129 | trace!("parse_decl");
130 | if self.at(TokenKind::Class) {
131 | self.class_decl()
132 | } else if self.at(TokenKind::Func) {
133 | self.func_decl(false)
134 | } else if self.at(TokenKind::Let) {
135 | self.var_decl()
136 | } else if self.at(TokenKind::Enum) {
137 | self.enum_decl()
138 | } else {
139 | self.stmt()
140 | }
141 | }
142 |
143 | // Enums are just syntactical sugar for static classes
144 | fn enum_decl(&mut self) -> Result {
145 | trace!("parse_enum_decl");
146 | let enum_token_span = self.expect(TokenKind::Enum)?.span;
147 | let name_token = self.expect(TokenKind::Ident)?;
148 |
149 | let spanned_name: Spanned = name_token.into();
150 |
151 | let name = name_token.text.to_string();
152 | let name_span = name_token.span;
153 |
154 | self.expect(TokenKind::LeftBrace)?;
155 |
156 | let mut statics = Vec::new();
157 | let mut static_symbols = HashMap::default();
158 |
159 | if !self.at(TokenKind::RightBrace) {
160 | loop {
161 | let enum_kind = self.expect(TokenKind::Ident)?;
162 | let stmt = Statement {
163 | stmt: Stmt::Variable {
164 | name: Binding {
165 | name: enum_kind.into(),
166 | ty: Type::Int,
167 | },
168 | expr: Some(Expression {
169 | expr: Expr::Int(statics.len() as i64),
170 | span: Span::garbage(),
171 | ty: Type::Int,
172 | }),
173 | },
174 | span: enum_kind.span,
175 | };
176 |
177 | static_symbols.insert(
178 | enum_kind.text.clone(),
179 | Symbol {
180 | sym: Sym::Variable,
181 | name: enum_kind.into(),
182 | ty: Type::Int,
183 | },
184 | );
185 |
186 | statics.push(stmt);
187 |
188 | if self.at(TokenKind::Comma) {
189 | self.bump()?;
190 | } else {
191 | break;
192 | }
193 | }
194 | }
195 |
196 | self.symtab.insert(Symbol {
197 | name: spanned_name,
198 | ty: Type::Class(name.clone()),
199 | sym: Sym::Class {
200 | methods: HashMap::default(),
201 | statics: static_symbols,
202 | },
203 | });
204 |
205 | let right_brace = self.expect(TokenKind::RightBrace)?;
206 |
207 | Ok(Statement {
208 | span: Span::combine(&[enum_token_span, right_brace.span]),
209 | stmt: Stmt::Class(Class {
210 | name: Spanned {
211 | item: name,
212 | span: name_span,
213 | },
214 | methods: vec![],
215 | statics,
216 | }),
217 | })
218 | }
219 |
220 | fn class_decl(&mut self) -> Result {
221 | trace!("parse_class_decl");
222 | let class_token_span = self.expect(TokenKind::Class)?.span;
223 | let name_token = self.expect(TokenKind::Ident)?;
224 | let spanned_name: Spanned = name_token.into();
225 |
226 | let name = name_token.text.to_string();
227 | let name_span = name_token.span;
228 |
229 | self.expect(TokenKind::LeftBrace)?;
230 |
231 | let mut methods = Vec::new();
232 | let mut method_symbols = HashMap::default();
233 |
234 | let mut statics = Vec::new();
235 | let mut static_symbols = HashMap::default();
236 |
237 | while !self.source.at_end() && !self.at(TokenKind::RightBrace) {
238 | if self.at(TokenKind::Static) {
239 | let static_token_span = self.bump()?.span;
240 | let binding = self.binding()?;
241 |
242 | let mut ty = binding.ty.clone();
243 | let mut expr = None;
244 | if self.at(TokenKind::Equal) {
245 | self.bump()?;
246 | let val = self.expr()?;
247 | if !type_compatible(&ty, &val.ty) {
248 | return Err(ParseError::IncompatibleTypes(
249 | self.source.id,
250 | binding.name.span.into(),
251 | binding.ty,
252 | val.span.into(),
253 | val.ty,
254 | ));
255 | }
256 |
257 | ty = val.ty.clone();
258 | expr = Some(val);
259 | }
260 |
261 | static_symbols.insert(
262 | binding.name.item.clone(),
263 | Symbol {
264 | sym: Sym::Variable,
265 | name: binding.name.clone(),
266 | ty,
267 | },
268 | );
269 |
270 | let semi_token = self.expect(TokenKind::Semicolon)?;
271 | statics.push(Statement {
272 | span: Span::combine(&[static_token_span, semi_token.span]),
273 | stmt: Stmt::Variable {
274 | name: binding,
275 | expr,
276 | },
277 | });
278 | } else {
279 | let stmt = self.func_decl(true)?;
280 | match stmt.clone().stmt {
281 | Stmt::Function(func) => {
282 | methods.push(stmt);
283 | method_symbols.insert(
284 | func.name.item.clone(),
285 | Symbol {
286 | name: func.name.clone(),
287 | sym: Sym::Function {
288 | arity: func.params.len(),
289 | },
290 | ty: func.ty.ty,
291 | },
292 | );
293 | }
294 | _ => panic!("ICE: func_decl returned a stmt that wasnt a function"),
295 | }
296 | }
297 | }
298 |
299 | self.symtab.insert(Symbol {
300 | name: spanned_name,
301 | ty: Type::Class(name.clone()),
302 | sym: Sym::Class {
303 | methods: method_symbols,
304 | statics: static_symbols,
305 | },
306 | });
307 |
308 | let right_brace = self.expect(TokenKind::RightBrace)?;
309 |
310 | Ok(Statement {
311 | span: Span::combine(&[class_token_span, right_brace.span]),
312 | stmt: Stmt::Class(Class {
313 | name: Spanned {
314 | item: name,
315 | span: name_span,
316 | },
317 | methods,
318 | statics,
319 | }),
320 | })
321 | }
322 |
323 | fn func_decl(&mut self, from_class_decl: bool) -> Result {
324 | trace!("parse_func_decl");
325 | let mut start_span: Span = Span::garbage();
326 | if !from_class_decl {
327 | start_span = self.expect(TokenKind::Func)?.span;
328 | }
329 |
330 | let name_token = self.expect(TokenKind::Ident)?;
331 | let spanned_name = Spanned {
332 | item: name_token.text.to_string(),
333 | span: name_token.span,
334 | };
335 | let name = name_token.text.to_string();
336 |
337 | if from_class_decl {
338 | start_span = name_token.span;
339 | }
340 |
341 | self.expect(TokenKind::LeftParen)?;
342 |
343 | let mut params = Vec::new();
344 | if !self.at(TokenKind::RightParen) {
345 | loop {
346 | let param = self.binding()?;
347 | params.push(param);
348 |
349 | if self.at(TokenKind::Comma) {
350 | self.bump()?;
351 | } else {
352 | break;
353 | }
354 | }
355 | }
356 |
357 | let right_paren_span = self.expect(TokenKind::RightParen)?.span;
358 |
359 | // Check if type defined
360 | let mut return_ty = TypeExpression {
361 | span: Span::garbage(),
362 | ty: Type::Any,
363 | };
364 |
365 | if self.at(TokenKind::Arrow) {
366 | self.bump()?;
367 | return_ty = self.ty()?;
368 | }
369 |
370 | let func_type = Type::Function {
371 | params: params
372 | .clone()
373 | .into_iter()
374 | .map(|param| param.into())
375 | .collect(),
376 | returns: Box::new(return_ty.clone()),
377 | };
378 |
379 | if !from_class_decl {
380 | self.symtab.insert(Symbol {
381 | name: spanned_name.clone(),
382 | sym: Sym::Function {
383 | arity: params.len(),
384 | },
385 | ty: func_type.clone(),
386 | });
387 | }
388 |
389 | self.symtab.level_up();
390 |
391 | params.iter().for_each(|param| {
392 | self.symtab.insert(Symbol {
393 | name: param.name.clone(),
394 | sym: Sym::Variable,
395 | ty: param.ty.clone(),
396 | })
397 | });
398 |
399 | let body = self.block(true)?;
400 |
401 | self.symtab.level_down();
402 |
403 | if let Stmt::Block(stmts) = &body.stmt {
404 | // If the body has a return statement in it, make sure the types line up
405 | if let Some(Statement {
406 | stmt: Stmt::Return(ret),
407 | ..
408 | }) = stmts.last()
409 | {
410 | if let Some(ret_expr) = ret {
411 | if !type_compatible(&ret_expr.ty, &return_ty.ty) {
412 | return Err(ParseError::IncompatibleTypes(
413 | self.source.id,
414 | return_ty.span.into(),
415 | return_ty.ty,
416 | ret_expr.span.into(),
417 | ret_expr.ty.clone(),
418 | ));
419 | } else {
420 | // Update type to the type of the return stmt
421 | if !from_class_decl {
422 | let sym = self
423 | .symtab
424 | .lookup_mut(&name)
425 | .expect("ICE: couldn't find func symbol to update ret type");
426 | if let Type::Function { params, returns } = sym.ty.clone() {
427 | sym.ty = Type::Function {
428 | params,
429 | returns: Box::new(TypeExpression {
430 | span: returns.span,
431 | ty: return_ty.ty,
432 | }),
433 | };
434 | } else {
435 | panic!("ICE: function type in symtab is not Type::Function");
436 | }
437 | }
438 | }
439 | }
440 | } else {
441 | // If the body has no return statement, make sure there is no return type
442 | // annotation on the function
443 | if !matches!(return_ty.ty, Type::Any | Type::Null) {
444 | return Err(ParseError::FunctionHasIncompatibleReturnType(
445 | self.source.id,
446 | return_ty.span.into(),
447 | return_ty.ty,
448 | body.span.past().into(),
449 | Type::Null,
450 | ));
451 | }
452 | }
453 | }
454 |
455 | Ok(Statement {
456 | stmt: Stmt::Function(Function {
457 | ty: TypeExpression {
458 | ty: func_type,
459 | span: spanned_name.span,
460 | },
461 | name: spanned_name,
462 | params,
463 | body: Box::new(body),
464 | }),
465 | span: Span::combine(&[start_span, right_paren_span]),
466 | })
467 | }
468 |
469 | fn var_decl(&mut self) -> Result {
470 | trace!("parse_var_decl");
471 | let let_token_span = self.bump()?.span;
472 |
473 | let binding = self.binding()?;
474 |
475 | let mut ty = binding.ty.clone();
476 | let mut expr = None;
477 | if self.at(TokenKind::Equal) {
478 | self.bump()?;
479 | let val = self.expr()?;
480 | if !type_compatible(&ty, &val.ty) {
481 | return Err(ParseError::IncompatibleTypes(
482 | self.source.id,
483 | binding.name.span.into(),
484 | binding.ty,
485 | val.span.into(),
486 | val.ty,
487 | ));
488 | }
489 |
490 | ty = val.ty.clone();
491 | expr = Some(val);
492 | }
493 |
494 | self.symtab.insert(Symbol {
495 | sym: Sym::Variable,
496 | name: binding.name.clone(),
497 | ty,
498 | });
499 |
500 | let semi_token = self.expect(TokenKind::Semicolon)?;
501 | Ok(Statement {
502 | span: Span::combine(&[let_token_span, semi_token.span]),
503 | stmt: Stmt::Variable {
504 | name: binding,
505 | expr,
506 | },
507 | })
508 | }
509 |
510 | fn stmt(&mut self) -> Result {
511 | trace!("parse_stmt");
512 | if self.at(TokenKind::LeftBrace) {
513 | self.block(false)
514 | } else if self.at(TokenKind::If) {
515 | self.if_stmt()
516 | } else if self.at(TokenKind::Until) {
517 | self.until_stmt()
518 | } else if self.at(TokenKind::Ret) {
519 | self.ret_stmt()
520 | } else {
521 | self.expr_stmt()
522 | }
523 | }
524 |
525 | fn ret_stmt(&mut self) -> Result {
526 | trace!("parse_ret_stmt");
527 | let ret_span = self.expect(TokenKind::Ret)?.span;
528 |
529 | if self.symtab.at_global_scope() {
530 | return Err(ParseError::CantReturnFromGlobalScope(
531 | self.source.id,
532 | ret_span.into(),
533 | ));
534 | }
535 |
536 | let mut expr: Option = None;
537 | if !self.at(TokenKind::Semicolon) {
538 | expr = Some(self.expr()?);
539 | }
540 |
541 | let semi_colon_span = self.bump()?.span;
542 |
543 | Ok(Statement {
544 | stmt: Stmt::Return(expr),
545 | span: Span::combine(&[ret_span, semi_colon_span]),
546 | })
547 | }
548 |
549 | fn until_stmt(&mut self) -> Result {
550 | trace!("parse_until_stmt");
551 | let until_token_span = self.expect(TokenKind::Until)?.span;
552 |
553 | let cond = self.expr()?;
554 |
555 | let body = self.stmt()?;
556 |
557 | Ok(Statement {
558 | span: Span::combine(&[until_token_span, body.span]),
559 | stmt: Stmt::Until {
560 | cond,
561 | body: Box::new(body),
562 | },
563 | })
564 | }
565 |
566 | fn if_stmt(&mut self) -> Result {
567 | trace!("parse_if_stmt");
568 | let if_token_span = self.expect(TokenKind::If)?.span;
569 |
570 | //self.expect(TokenKind::LeftParen)?;
571 | let cond = self.expr()?;
572 | //self.expect(TokenKind::RightParen)?;
573 |
574 | let body = self.stmt()?;
575 |
576 | let mut else_branch = None;
577 | if self.at(TokenKind::Else) {
578 | self.bump()?;
579 | else_branch = Some(Box::new(self.stmt()?));
580 | }
581 |
582 | Ok(Statement {
583 | span: Span::combine(&[if_token_span, body.span]),
584 | stmt: Stmt::If {
585 | cond,
586 | body: Box::new(body),
587 | else_branch,
588 | },
589 | })
590 | }
591 |
592 | fn block(&mut self, from_func_decl: bool) -> Result {
593 | trace!("parse_block");
594 | let left_brace_span = self.expect(TokenKind::LeftBrace)?.span;
595 |
596 | if !from_func_decl {
597 | self.symtab.level_up();
598 | }
599 |
600 | let mut stmts = Vec::new();
601 | while !self.source.at_end() && !self.at(TokenKind::RightBrace) {
602 | stmts.push(self.decl()?);
603 | }
604 |
605 | let right_brace_span = self.expect(TokenKind::RightBrace)?.span;
606 |
607 | if !from_func_decl {
608 | self.symtab.level_down();
609 | }
610 |
611 | Ok(Statement {
612 | stmt: Stmt::Block(stmts),
613 | span: Span::combine(&[left_brace_span, right_brace_span]),
614 | })
615 | }
616 |
617 | fn expr_stmt(&mut self) -> Result {
618 | trace!("parse_expr_stmt");
619 | let expr = self.expr()?;
620 | let semi = self.expect(TokenKind::Semicolon)?;
621 | Ok(Statement {
622 | span: Span::combine(&[expr.span, semi.span]),
623 | stmt: Stmt::Expr(expr),
624 | })
625 | }
626 |
627 | fn expr(&mut self) -> Result {
628 | trace!("parse_expr");
629 | self.assignment()
630 | }
631 |
632 | fn assignment(&mut self) -> Result {
633 | trace!("parse_assignment");
634 | let expr = self.or()?;
635 |
636 | if self.at(TokenKind::Equal) {
637 | let error_source = self.source.id;
638 | let eq_span = self.bump()?.span;
639 |
640 | let rhs = self.assignment()?;
641 |
642 | return match expr.expr {
643 | Expr::Variable(name) => {
644 | if let Some(entry) = self.symtab.lookup_mut(&name) {
645 | if type_compatible(&entry.ty, &rhs.ty) {
646 | (*entry).ty = rhs.ty.clone();
647 |
648 | Ok(Expression {
649 | ty: rhs.ty.clone(),
650 | expr: Expr::Assign {
651 | name: Spanned {
652 | item: name,
653 | span: expr.span,
654 | },
655 | rhs: Box::new(rhs),
656 | },
657 | span: expr.span,
658 | })
659 | } else {
660 | Err(ParseError::IncompatibleTypes(
661 | self.source.id,
662 | expr.span.into(),
663 | entry.ty.clone(),
664 | rhs.span.into(),
665 | rhs.ty,
666 | ))
667 | }
668 | } else {
669 | Err(ParseError::UndeclaredVariable(
670 | self.source.id,
671 | expr.span.into(),
672 | name,
673 | ))
674 | }
675 | }
676 | Expr::Get { object, name } => Ok(Expression {
677 | span: Span::combine(&[expr.span, rhs.span]),
678 | expr: Expr::Set {
679 | object,
680 | name,
681 | rhs: Box::new(rhs),
682 | },
683 | ty: Type::Null,
684 | }),
685 | Expr::IndexGet { lhs, index } => Ok(Expression {
686 | span: Span::combine(&[expr.span, rhs.span]),
687 | expr: Expr::IndexSet {
688 | lhs,
689 | index,
690 | rhs: Box::new(rhs),
691 | },
692 | ty: Type::Null,
693 | }),
694 | _ => Err(ParseError::InvalidAssignmentTarget(
695 | error_source,
696 | eq_span.into(),
697 | )),
698 | };
699 | }
700 |
701 | Ok(expr)
702 | }
703 |
704 | fn or(&mut self) -> Result {
705 | trace!("parse_or");
706 | let mut expr = self.and()?;
707 |
708 | while self.at(TokenKind::Or) {
709 | let op: Operator = self.bump()?.into();
710 | let rhs = self.and()?;
711 |
712 | let ty = self.result_type(&expr, &op, &rhs)?;
713 |
714 | expr = Expression {
715 | span: Span::combine(&[op.span, rhs.span]),
716 | ty,
717 | expr: Expr::Logical {
718 | lhs: Box::new(expr),
719 | op,
720 | rhs: Box::new(rhs),
721 | },
722 | }
723 | }
724 |
725 | Ok(expr)
726 | }
727 |
728 | fn and(&mut self) -> Result {
729 | trace!("parse_and");
730 | let mut expr = self.equality()?;
731 |
732 | while self.at(TokenKind::And) {
733 | let op: Operator = self.bump()?.into();
734 | let rhs = self.equality()?;
735 |
736 | let ty = self.result_type(&expr, &op, &rhs)?;
737 |
738 | expr = Expression {
739 | span: Span::combine(&[op.span, rhs.span]),
740 | ty,
741 | expr: Expr::Logical {
742 | lhs: Box::new(expr),
743 | op,
744 | rhs: Box::new(rhs),
745 | },
746 | };
747 | }
748 |
749 | Ok(expr)
750 | }
751 |
752 | fn equality(&mut self) -> Result {
753 | trace!("parse_equality");
754 | let mut expr = self.comparison()?;
755 |
756 | while self.at_set(&[TokenKind::BangEqual, TokenKind::EqualEqual]) {
757 | let op: Operator = self.bump()?.into();
758 | let rhs = self.comparison()?;
759 |
760 | let ty = self.result_type(&expr, &op, &rhs)?;
761 |
762 | expr = Expression {
763 | span: Span::combine(&[op.span, rhs.span]),
764 | ty,
765 | expr: Expr::Binary {
766 | lhs: Box::new(expr),
767 | op,
768 | rhs: Box::new(rhs),
769 | },
770 | };
771 | }
772 |
773 | Ok(expr)
774 | }
775 |
776 | fn comparison(&mut self) -> Result {
777 | trace!("parse_comparison");
778 | let mut expr = self.term()?;
779 |
780 | while self.at_set(&[
781 | TokenKind::Greater,
782 | TokenKind::GreaterEqual,
783 | TokenKind::Less,
784 | TokenKind::LessEqual,
785 | ]) {
786 | let op: Operator = self.bump()?.into();
787 | let rhs = self.term()?;
788 |
789 | let ty = self.result_type(&expr, &op, &rhs)?;
790 |
791 | expr = Expression {
792 | span: Span::combine(&[op.span, rhs.span]),
793 | ty,
794 | expr: Expr::Binary {
795 | lhs: Box::new(expr),
796 | op,
797 | rhs: Box::new(rhs),
798 | },
799 | };
800 | }
801 |
802 | Ok(expr)
803 | }
804 |
805 | fn term(&mut self) -> Result {
806 | trace!("parse_term");
807 | let mut expr = self.factor()?;
808 |
809 | while self.at_set(&[TokenKind::Minus, TokenKind::Plus]) {
810 | let op = self.bump()?.into();
811 | let rhs = self.factor()?;
812 |
813 | let ty = self.result_type(&expr, &op, &rhs)?;
814 |
815 | expr = Expression {
816 | span: Span::combine(&[expr.span, rhs.span]),
817 | ty,
818 | expr: Expr::Binary {
819 | lhs: Box::new(expr),
820 | op,
821 | rhs: Box::new(rhs),
822 | },
823 | };
824 | }
825 |
826 | Ok(expr)
827 | }
828 |
829 | fn factor(&mut self) -> Result {
830 | trace!("parse_factor");
831 | let mut expr = self.unary()?;
832 |
833 | while self.at_set(&[TokenKind::Slash, TokenKind::Star]) {
834 | let op = self.bump()?.into();
835 | let rhs = self.unary()?;
836 |
837 | let ty = self.result_type(&expr, &op, &rhs)?;
838 |
839 | expr = Expression {
840 | span: Span::combine(&[expr.span, rhs.span]),
841 | expr: Expr::Binary {
842 | lhs: Box::new(expr),
843 | op,
844 | rhs: Box::new(rhs),
845 | },
846 | ty,
847 | };
848 | }
849 |
850 | Ok(expr)
851 | }
852 |
853 | fn unary(&mut self) -> Result {
854 | trace!("parse_unary");
855 | if self.at_set(&[TokenKind::Bang, TokenKind::Minus]) {
856 | let op: Operator = self.bump()?.into();
857 | let rhs = self.unary()?;
858 |
859 | if op.op == Op::Not && rhs.ty != Type::Bool {
860 | return Err(ParseError::UnsupportedUnaryOperation(
861 | self.source.id,
862 | op.span.into(),
863 | rhs.span.into(),
864 | rhs.ty,
865 | ));
866 | }
867 |
868 | if op.op == Op::Sub && (rhs.ty != Type::Float && rhs.ty != Type::Int) {
869 | return Err(ParseError::UnsupportedUnaryOperation(
870 | self.source.id,
871 | op.span.into(),
872 | rhs.span.into(),
873 | rhs.ty,
874 | ));
875 | }
876 |
877 | Ok(Expression {
878 | span: Span::combine(&[op.span, rhs.span]),
879 | ty: rhs.ty.clone(),
880 | expr: Expr::Unary {
881 | op,
882 | rhs: Box::new(rhs),
883 | },
884 | })
885 | } else {
886 | self.index()
887 | }
888 | }
889 |
890 | fn index(&mut self) -> Result {
891 | trace!("parse_index");
892 | let mut expr = self.call()?;
893 | if self.at(TokenKind::LeftBracket) {
894 | self.bump()?;
895 | let index_expr = self.expr()?;
896 | let end_span = self.expect(TokenKind::RightBracket)?.span;
897 |
898 | // TODO type checking for indices
899 |
900 | expr = Expression {
901 | span: Span::combine(&[expr.span, end_span]),
902 | ty: Type::Any,
903 | expr: Expr::IndexGet {
904 | lhs: Box::new(expr),
905 | index: Box::new(index_expr),
906 | },
907 | }
908 | }
909 |
910 | Ok(expr)
911 | }
912 |
913 | fn call(&mut self) -> Result {
914 | trace!("parse_call");
915 | let mut expr = self.primary()?;
916 |
917 | loop {
918 | if self.at(TokenKind::LeftParen) {
919 | self.bump()?;
920 | expr = self.finish_call(expr)?;
921 | } else if self.at(TokenKind::Dot) {
922 | self.bump()?;
923 | let name = self.expect(TokenKind::Ident)?;
924 |
925 | // Can only use dot operator when left hand side is of type Instance or Class
926 | if !matches!(expr.ty, Type::Any | Type::Instance(..) | Type::Class(..)) {
927 | return Err(ParseError::OnlyInstancesAndClassesHaveProperties(
928 | self.source.id,
929 | expr.span.into(),
930 | expr.ty,
931 | ));
932 | }
933 |
934 | expr = Expression {
935 | span: Span::combine(&[expr.span, name.span]),
936 | ty: Type::Any,
937 | expr: Expr::Get {
938 | name: name.into(),
939 | object: Box::new(expr),
940 | },
941 | };
942 | } else {
943 | break;
944 | }
945 | }
946 |
947 | Ok(expr)
948 | }
949 |
950 | fn finish_call(&mut self, callee: Expression) -> Result {
951 | trace!("parse_finish_call");
952 | let mut args: Vec = Vec::new();
953 |
954 | // Check if we have args
955 | if !self.at(TokenKind::RightParen) {
956 | loop {
957 | args.push(self.expr()?);
958 |
959 | if self.at(TokenKind::Comma) {
960 | self.bump()?;
961 | } else {
962 | break;
963 | }
964 | }
965 | }
966 |
967 | let paren = self.expect(TokenKind::RightParen)?.span;
968 |
969 | trace!(format!("{:#?}", callee.expr));
970 |
971 | // TODO: type checking on get expr's for instances
972 | // Example: someInstance.foo()
973 |
974 | self.is_callable(&callee)?;
975 |
976 | let ty = match &callee.ty {
977 | Type::Class(class_name) => {
978 | // Type check class constructor
979 | let entry = self
980 | .symtab
981 | .lookup(class_name)
982 | .expect("ICE: parser should have checked if class was declared by this point");
983 | if let Sym::Class { methods, .. } = &entry.sym {
984 | if let Some(constructor) = methods.get("constructor") {
985 | if let Type::Function { params, .. } = &constructor.ty {
986 | for (param, arg) in params.iter().zip(args.iter()) {
987 | if !type_compatible(¶m.ty, &arg.ty) {
988 | return Err(ParseError::IncompatibleTypes(
989 | callee.span.source_id,
990 | param.span.into(),
991 | param.ty.clone(),
992 | arg.span.into(),
993 | arg.ty.clone(),
994 | ));
995 | }
996 | }
997 | }
998 | }
999 |
1000 | Type::Instance(class_name.to_string())
1001 | } else {
1002 | panic!("ICE: callee symtab type and actual type are not the same");
1003 | }
1004 | }
1005 | Type::Function { returns, .. } => returns.ty.clone(),
1006 | _ => callee.ty.clone(),
1007 | };
1008 |
1009 | Ok(Expression {
1010 | span: Span::combine(&[callee.span, paren]),
1011 | ty,
1012 | expr: Expr::Call {
1013 | callee: Box::new(callee),
1014 | paren,
1015 | args,
1016 | },
1017 | })
1018 | }
1019 |
1020 | fn binding(&mut self) -> Result {
1021 | trace!("parse_binding");
1022 | let ident = self.expect(TokenKind::Ident)?;
1023 |
1024 | let name = ident.text.to_string();
1025 | let span = ident.span;
1026 |
1027 | if let Some(sym) = self.symtab.lookup(&name) {
1028 | return Err(ParseError::CannotRedeclareSymbol(
1029 | self.source.id,
1030 | sym.name.item.clone(),
1031 | span.into(),
1032 | sym.name.span.into(),
1033 | ));
1034 | }
1035 |
1036 | let mut ty = Type::Any;
1037 | if self.at(TokenKind::Colon) {
1038 | self.bump()?;
1039 |
1040 | ty = self.ty()?.ty;
1041 | }
1042 |
1043 | Ok(Binding {
1044 | name: Spanned { item: name, span },
1045 | ty,
1046 | })
1047 | }
1048 |
1049 | fn ty(&mut self) -> Result {
1050 | trace!("parse_ty");
1051 | let token = self.bump()?;
1052 | let span = token.span;
1053 |
1054 | let ty = match token.kind {
1055 | TokenKind::TypeInt => Type::Int,
1056 | TokenKind::TypeFloat => Type::Float,
1057 | TokenKind::TypeBool => Type::Bool,
1058 | TokenKind::TypeString => Type::String,
1059 | TokenKind::Null => Type::Null,
1060 | TokenKind::TypeAny => Type::Any,
1061 | TokenKind::Ident => Type::Instance(token.text.clone()),
1062 | TokenKind::LeftBracket => {
1063 | // array types. Ex: [int]
1064 | let list_ty = self.ty()?;
1065 | let end_span = self.expect(TokenKind::RightBracket)?.span;
1066 | return Ok(TypeExpression {
1067 | ty: Type::List(Box::new(list_ty)),
1068 | span: Span::combine(&[span, end_span]),
1069 | });
1070 | }
1071 | TokenKind::LeftParen => {
1072 | // function type. Ex: (int, int) -> int
1073 | let mut params = Vec::new();
1074 | if !self.at(TokenKind::RightParen) {
1075 | loop {
1076 | let param = self.ty()?;
1077 | params.push(param);
1078 |
1079 | if self.at(TokenKind::Comma) {
1080 | self.bump()?;
1081 | } else {
1082 | break;
1083 | }
1084 | }
1085 | }
1086 |
1087 | self.expect(TokenKind::RightParen)?;
1088 |
1089 | // Function types must define return type
1090 | self.expect(TokenKind::Arrow)?;
1091 | let returns = self.ty()?;
1092 |
1093 | return Ok(TypeExpression {
1094 | span: Span::combine(&[span, returns.span]),
1095 | ty: Type::Function {
1096 | params,
1097 | returns: Box::new(returns),
1098 | },
1099 | });
1100 | }
1101 | _ => return Err(ParseError::UnknownType(self.source.id, span.into())),
1102 | };
1103 |
1104 | Ok(TypeExpression { ty, span })
1105 | }
1106 |
1107 | fn primary(&mut self) -> Result {
1108 | trace!("parse_primary");
1109 | trace!(format!("symtab before lookup: {:#?}", self.symtab.clone()));
1110 | let token = self.bump()?;
1111 | let token_span = token.span;
1112 |
1113 | match token.kind {
1114 | TokenKind::False => Ok(Expression {
1115 | expr: Expr::Bool(false),
1116 | span: token.span,
1117 | ty: Type::Bool,
1118 | }),
1119 | TokenKind::True => Ok(Expression {
1120 | expr: Expr::Bool(true),
1121 | span: token.span,
1122 | ty: Type::Bool,
1123 | }),
1124 | TokenKind::Null => Ok(Expression {
1125 | expr: Expr::Null,
1126 | span: token.span,
1127 | ty: Type::Null,
1128 | }),
1129 | TokenKind::This => Ok(Expression {
1130 | expr: Expr::This,
1131 | span: token_span,
1132 | ty: Type::Any,
1133 | }),
1134 | TokenKind::LeftParen => {
1135 | let span = token.span;
1136 | let expr = self.expr()?;
1137 |
1138 | let right_paren = self.expect(TokenKind::RightParen)?;
1139 | Ok(Expression {
1140 | ty: expr.ty.clone(),
1141 | expr: Expr::Grouping(Box::new(expr)),
1142 | span: Span::combine(&[span, right_paren.span]),
1143 | })
1144 | }
1145 | TokenKind::Ident => {
1146 | let ident = token.text.to_string();
1147 | let span = token.span;
1148 | if let Some(symbol) = self.symtab.lookup(&ident) {
1149 | Ok(Expression {
1150 | expr: Expr::Variable(ident),
1151 | span,
1152 | ty: symbol.ty.clone(),
1153 | })
1154 | } else {
1155 | Err(ParseError::UndeclaredVariable(
1156 | self.source.id,
1157 | span.into(),
1158 | ident,
1159 | ))
1160 | }
1161 | }
1162 | TokenKind::Int => {
1163 | let val = token
1164 | .text
1165 | .parse::()
1166 | .expect("ICE: Couldn't parse int as int");
1167 | Ok(Expression {
1168 | expr: Expr::Int(val),
1169 | span: token.span,
1170 | ty: Type::Int,
1171 | })
1172 | }
1173 | TokenKind::Float => {
1174 | let val = token
1175 | .text
1176 | .parse::()
1177 | .expect("ICE: Couldn't parse float as float");
1178 | Ok(Expression {
1179 | expr: Expr::Float(val),
1180 | span: token.span,
1181 | ty: Type::Float,
1182 | })
1183 | }
1184 | TokenKind::String => {
1185 | // Trim the first and last char, as they are " characters
1186 | let mut token_text = token.text.to_string();
1187 | token_text.remove(0);
1188 | token_text.remove(token_text.len() - 1);
1189 |
1190 | Ok(Expression {
1191 | expr: Expr::String(token_text),
1192 | span: token.span,
1193 | ty: Type::String,
1194 | })
1195 | }
1196 | TokenKind::LeftBracket => {
1197 | let start_span = token.span;
1198 |
1199 | let mut exprs = vec![];
1200 | if !self.at(TokenKind::RightBracket) {
1201 | loop {
1202 | let expr = self.expr()?;
1203 |
1204 | // It's actually list shorthand expression
1205 | if self.at(TokenKind::Semicolon) {
1206 | self.bump()?;
1207 | let count = self.expr()?;
1208 |
1209 | if !type_compatible(&count.ty, &Type::Int) {
1210 | return Err(ParseError::ListShorthandCountMustBeInt(
1211 | count.span.source_id,
1212 | count.span.into(),
1213 | ));
1214 | }
1215 |
1216 | let end_span = self.expect(TokenKind::RightBracket)?.span;
1217 |
1218 | return Ok(Expression {
1219 | ty: Type::List(Box::new(TypeExpression {
1220 | ty: expr.ty.clone(),
1221 | span: expr.span,
1222 | })),
1223 | expr: Expr::ListShorthand {
1224 | value: Box::new(expr),
1225 | count: Box::new(count),
1226 | },
1227 | span: Span::combine(&[start_span, end_span]),
1228 | });
1229 | } else {
1230 | exprs.push(expr);
1231 | if self.at(TokenKind::Comma) {
1232 | self.bump()?;
1233 | } else {
1234 | break;
1235 | }
1236 | }
1237 | }
1238 | }
1239 |
1240 | let end_span = self.expect(TokenKind::RightBracket)?.span;
1241 |
1242 | // We do type checking on the array after creating it because we need the span of
1243 | // the entire array for error messages
1244 | let mut list_ty = TypeExpression {
1245 | ty: Type::Any,
1246 | span: Span::combine(&[start_span, end_span]),
1247 | };
1248 | for expr in &exprs {
1249 | if !type_compatible(&list_ty.ty, &expr.ty) {
1250 | return Err(ParseError::IncompatibleTypes(
1251 | expr.span.source_id,
1252 | list_ty.span.into(),
1253 | list_ty.ty,
1254 | expr.span.into(),
1255 | expr.ty.clone(),
1256 | ));
1257 | } else {
1258 | // coerce list type
1259 | list_ty.ty = expr.ty.clone();
1260 | }
1261 | }
1262 |
1263 | Ok(Expression {
1264 | expr: Expr::List(exprs),
1265 | span: Span::combine(&[start_span, end_span]),
1266 | ty: Type::List(Box::new(list_ty)),
1267 | })
1268 | }
1269 | _ => Err(ParseError::ExpectedExpression(
1270 | self.source.id,
1271 | token_span.into(),
1272 | )),
1273 | }
1274 | }
1275 | }
1276 |
--------------------------------------------------------------------------------