├── 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 | [![CI](https://github.com/nakala-lang/nakala/actions/workflows/CI.yml/badge.svg)](https://github.com/nakala-lang/nakala/actions/workflows/CI.yml) 6 | ![PRs Welcome](https://img.shields.io/badge/PRs-welcomed-green.svg) 7 | [![GitHub license](https://img.shields.io/github/license/nakala-lang/nakala.svg)](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 | --------------------------------------------------------------------------------