Compare commits
25 Commits
feature-fu
...
non-enum-b
| Author | SHA1 | Date | |
|---|---|---|---|
| 6096bb431a | |||
| 85211b127d | |||
| 02f258415d | |||
| 5eae0712bf | |||
| e28b3c4f37 | |||
| 9e3a642810 | |||
| e62121c75b | |||
| ffdce64df8 | |||
| d2daa7ae6d | |||
| abf9eb73c8 | |||
| 39b55b51da | |||
| 5bf989a640 | |||
| 3dacee0be4 | |||
| d035724d20 | |||
| 24f5aa30ea | |||
| 2a014fd210 | |||
| 788c4a8e82 | |||
| 7646177030 | |||
| b128b3357a | |||
| 4d5188d9d6 | |||
| e28a990b85 | |||
| 1f1f589dd4 | |||
| 6816392173 | |||
| 3c6fb5466e | |||
| 74dbf724a5 |
@@ -4,5 +4,3 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.53"
|
|
||||||
thiserror = "1.0.30"
|
|
||||||
|
|||||||
207
README.md
207
README.md
@@ -1,195 +1,38 @@
|
|||||||
# NEK-Lang
|
# NEK-Lang
|
||||||
|
|
||||||
## Variables
|
|
||||||
Currently all variables are global and completely unscoped. That means no matter where a variable is declared, it remains over the whole remaining runtime of the progam.
|
|
||||||
|
|
||||||
All variables are currently of type `i64` (64-bit signed integer)
|
|
||||||
|
|
||||||
### Declaration
|
|
||||||
- Declare and initialize a new variable
|
|
||||||
- Declaring a previously declared variable again is currently equivalent to an assignment
|
|
||||||
- Declaration is needed before assignment or other usage
|
|
||||||
- The variable name is on the left side of the `<-` operator
|
|
||||||
- The assigned value is on the right side and can be any expression
|
|
||||||
```
|
|
||||||
a <- 123;
|
|
||||||
```
|
|
||||||
Create a new variable named `a` and assign the value `123` to it.
|
|
||||||
|
|
||||||
### Assignment
|
|
||||||
- Assigning a value to a previously declared variable
|
|
||||||
- The variable name is on the left side of the `=` operator
|
|
||||||
- The assigned value is on the right side and can be any expression
|
|
||||||
```
|
|
||||||
a = 123;
|
|
||||||
```
|
|
||||||
The value `123` is assigned to the variable named `a`. `a` needs to be declared before this.
|
|
||||||
|
|
||||||
## Expressions
|
|
||||||
The operator precedence is the same order as in `C` for all implemented operators.
|
|
||||||
Refer to the
|
|
||||||
[C Operator Precedence Table](https://en.cppreference.com/w/c/language/operator_precedence)
|
|
||||||
to see the different precedences.
|
|
||||||
|
|
||||||
### General
|
|
||||||
- Parentheses `(` and `)` can be used to modify evaluation oder just like in any other
|
|
||||||
programming language.
|
|
||||||
- For example `(a + b) * c` will evaluate the addition before the multiplication, despite the multiplication having higher binding power
|
|
||||||
|
|
||||||
### Mathematical Operators
|
|
||||||
Supported mathematical operations:
|
|
||||||
- Addition `a + b`
|
|
||||||
- Subtraction `a - b`
|
|
||||||
- Multiplication `a * b`
|
|
||||||
- Division `a / b`
|
|
||||||
- Modulo `a % b`
|
|
||||||
- Negation `-a`
|
|
||||||
|
|
||||||
### Bitwise Operators
|
|
||||||
- And `a & b`
|
|
||||||
- Or `a | b`
|
|
||||||
- Xor `a ^ b`
|
|
||||||
- Bitshift left (by `b` bits) `a << b`
|
|
||||||
- Bitshift right (by `b` bits) `a >> b`
|
|
||||||
- "Bit flip" (One's complement) `~a`
|
|
||||||
|
|
||||||
### Logical Operators
|
|
||||||
The logical operators evaluate the operands as `false` if they are equal to `0` and `true` if they are not equal to `0`
|
|
||||||
- And `a && b`
|
|
||||||
- Or `a || b`
|
|
||||||
- Not `!a` (if `a` is equal to `0`, the result is `1`, otherwise the result is `0`)
|
|
||||||
|
|
||||||
### Equality & Relational Operators
|
|
||||||
The equality and relational operations result in `1` if the condition is evaluated as `true` and in `0` if the condition is evaluated as `false`.
|
|
||||||
- Equality `a == b`
|
|
||||||
- Inequality `a != b`
|
|
||||||
- Greater than `a > b`
|
|
||||||
- Greater or equal than `a >= b`
|
|
||||||
- Less than `a < b`
|
|
||||||
- Less or equal than `a <= b`
|
|
||||||
|
|
||||||
## Control-Flow
|
|
||||||
For conditions like in if or loops, every non zero value is equal to `true`, and `0` is `false`.
|
|
||||||
|
|
||||||
### Loop
|
|
||||||
- There is currently only the `loop` keyword that can act like a `while` with optional advancement (an expression that is executed after the loop body)
|
|
||||||
- The `loop` keyword is followed by the condition (an expression) without needing parentheses
|
|
||||||
- *Optional:* If there is a `;` after the condition, there must be another expression which is used as the advancement
|
|
||||||
- The loops body is wrapped in braces (`{ }`) just like in C/C++
|
|
||||||
|
|
||||||
```
|
|
||||||
// Print the numbers from 0 to 9
|
|
||||||
|
|
||||||
// Without advancement
|
|
||||||
i <- 0;
|
|
||||||
loop i < 10 {
|
|
||||||
print i;
|
|
||||||
i = i - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// With advancement
|
|
||||||
k <- 0;
|
|
||||||
loop k < 10; k = k - 1 {
|
|
||||||
print k;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### If / Else
|
|
||||||
|
|
||||||
- The language supports `if` and an optional `else`
|
|
||||||
- After the `if` keyword must be the deciding condition, parentheses are not needed
|
|
||||||
- The block *if-true* block is wrapped in braces (`{ }`)
|
|
||||||
- *Optional:* If there is an `else` after the *if-block*, there must be a following *if-false*, aka. else block
|
|
||||||
```
|
|
||||||
a <- 1;
|
|
||||||
b <- 2;
|
|
||||||
if a == b {
|
|
||||||
// a is equal to b
|
|
||||||
print 1;
|
|
||||||
} else {
|
|
||||||
// a is not equal to b
|
|
||||||
print 0;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## IO
|
|
||||||
|
|
||||||
### Print
|
|
||||||
Printing is implemented via the `print` keyword
|
|
||||||
- The `print` keyword is followed by an expression, the value of which will be printed to the terminal.
|
|
||||||
- Print currently automatically adds a linebreak
|
|
||||||
```
|
|
||||||
a <- 1;
|
|
||||||
print a; // Outputs `"1\n"` to the terminal
|
|
||||||
```
|
|
||||||
|
|
||||||
## Comments
|
|
||||||
|
|
||||||
### Line comments
|
|
||||||
Line comments can be initiated by using `//`
|
|
||||||
- Everything after `//` up to the end of the current line is ignored and not parsed
|
|
||||||
```
|
|
||||||
// This is a comment
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
# Feature Tracker
|
|
||||||
|
|
||||||
## High level Components
|
## High level Components
|
||||||
|
|
||||||
- [x] Lexer: Transforms text into Tokens
|
- [x] Lexer: Transforms text into Tokens
|
||||||
- [x] Parser: Transforms Tokens into Abstract Syntax Tree
|
- [x] Parser: Transforms Tokens into Abstract Syntax Tree
|
||||||
- [x] Interpreter (tree-walk-interpreter): Walks the tree and evaluates the expressions / statements
|
- [x] Interpreter (tree-walk-interpreter): Walks the tree and evaluates the expressions / statements
|
||||||
|
- [ ] Abstract Syntax Tree Optimizer
|
||||||
|
|
||||||
## Language features
|
## Language features
|
||||||
|
|
||||||
- [x] General expressions
|
- [x] Math expressions
|
||||||
- [x] Arithmetic operations
|
- [x] Unary operators
|
||||||
- [x] Addition `a + b`
|
- [x] Negate `-X`
|
||||||
- [x] Subtraction `a - b`
|
- [x] Parentheses `(X+Y)*Z`
|
||||||
- [x] Multiplication `a * b`
|
|
||||||
- [x] Division `a / b`
|
|
||||||
- [x] Modulo `a % b
|
|
||||||
- [x] Negate `-a`
|
|
||||||
- [x] Parentheses `(a + b) * c`
|
|
||||||
- [x] Logical boolean operators
|
- [x] Logical boolean operators
|
||||||
- [x] Equal `a == b`
|
|
||||||
- [x] Not equal `a != b`
|
|
||||||
- [x] Greater than `a > b`
|
|
||||||
- [x] Less than `a < b`
|
|
||||||
- [x] Greater than or equal `a >= b`
|
|
||||||
- [x] Less than or equal `a <= b`
|
|
||||||
- [x] Logical operators
|
|
||||||
- [x] And `a && b`
|
|
||||||
- [x] Or `a || b`
|
|
||||||
- [x] Not `!a`
|
|
||||||
- [x] Bitwise operators
|
|
||||||
- [x] Bitwise AND `a & b`
|
|
||||||
- [x] Bitwise OR `a | b`
|
|
||||||
- [x] Bitwise XOR `a ^ b`
|
|
||||||
- [x] Bitwise NOT `~a`
|
|
||||||
- [x] Bitwise left shift `a << b`
|
|
||||||
- [x] Bitwise right shift `a >> b`
|
|
||||||
- [x] Variables
|
- [x] Variables
|
||||||
- [x] Declaration
|
- [x] Declaration
|
||||||
- [x] Assignment
|
- [x] Assignment
|
||||||
- [x] Statements with semicolon & Multiline programs
|
- [x] While loop `while X { ... }`
|
||||||
- [x] Control flow
|
- [x] If else statement `if X { ... } else { ... }`
|
||||||
- [x] While loop `while X { ... }`
|
- [x] If Statement
|
||||||
- [x] If else statement `if X { ... } else { ... }`
|
- [x] Else statement
|
||||||
- [x] If Statement
|
- [ ] Line comments `//`
|
||||||
- [x] Else statement
|
|
||||||
- [x] Line comments `//`
|
|
||||||
- [x] Strings
|
- [x] Strings
|
||||||
- [x] IO Intrinsics
|
- [x] For loops `for X; Y; Z { ... }`
|
||||||
|
- [ ] IO Intrinsics
|
||||||
- [x] Print
|
- [x] Print
|
||||||
|
- [ ] ReadLine
|
||||||
|
|
||||||
## Grammar
|
## Grammar
|
||||||
|
|
||||||
### Expressions
|
### Expressions
|
||||||
```
|
```
|
||||||
LITERAL = I64_LITERAL | STR_LITERAL
|
LITERAL = I64 | Str
|
||||||
expr_primary = LITERAL | IDENT | "(" expr ")" | "-" expr_primary | "~" expr_primary
|
expr_primary = LITERAL | IDENT | "(" expr ")" | "-" expr_primary
|
||||||
expr_mul = expr_primary (("*" | "/" | "%") expr_primary)*
|
expr_mul = expr_primary (("*" | "/" | "%") expr_primary)*
|
||||||
expr_add = expr_mul (("+" | "-") expr_mul)*
|
expr_add = expr_mul (("+" | "-") expr_mul)*
|
||||||
expr_shift = expr_add ((">>" | "<<") expr_add)*
|
expr_shift = expr_add ((">>" | "<<") expr_add)*
|
||||||
@@ -198,15 +41,17 @@ expr_equ = expr_rel (("==" | "!=") expr_rel)*
|
|||||||
expr_band = expr_equ ("&" expr_equ)*
|
expr_band = expr_equ ("&" expr_equ)*
|
||||||
expr_bxor = expr_band ("^" expr_band)*
|
expr_bxor = expr_band ("^" expr_band)*
|
||||||
expr_bor = expr_bxor ("|" expr_bxor)*
|
expr_bor = expr_bxor ("|" expr_bxor)*
|
||||||
expr_land = expr_bor ("&&" expr_bor)*
|
expr = expr_bor
|
||||||
expr_lor = expr_land ("||" expr_land)*
|
|
||||||
expr = expr_lor
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Statements
|
## Statements
|
||||||
|
```
|
||||||
|
stmt_expr = expr
|
||||||
|
stmt_let = "let" IDENT "=" expr
|
||||||
|
stmt_while = "while" expr "{" (stmt)* "}"
|
||||||
|
stmt_for = "for" stmt_let ";" expr ";" expr "{" (stmt)* "}"
|
||||||
|
stmt_if = "if" expr "{" (stmt)* "}" ( "else" "{" (stmt)* "}" )
|
||||||
|
stmt_dbgprint = "$$" expr
|
||||||
|
stmt_print = "$" expr
|
||||||
|
stmt = stmt_expr | stmt_let | stmt_while | stmt_for | stmt_if | stmt_dbgprint | stmt_print
|
||||||
```
|
```
|
||||||
stmt_if = "if" expr "{" stmt* "}" ("else" "{" stmt* "}")?
|
|
||||||
stmt_loop = "loop" expr (";" expr)? "{" stmt* "}"
|
|
||||||
stmt_expr = expr ";"
|
|
||||||
stmt = stmt_expr | stmt_loop
|
|
||||||
```
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
// If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9.
|
|
||||||
// The sum of these multiples is 23.
|
|
||||||
// Find the sum of all the multiples of 3 or 5 below 1000.
|
|
||||||
//
|
|
||||||
// Correct Answer: 233168
|
|
||||||
|
|
||||||
sum <- 0;
|
|
||||||
i <- 0;
|
|
||||||
loop i < 1_000; i = i + 1 {
|
|
||||||
if i % 3 == 0 | i % 5 == 0 {
|
|
||||||
sum = sum + i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
print sum;
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
// Each new term in the Fibonacci sequence is generated by adding the previous two terms.
|
|
||||||
// By starting with 1 and 2, the first 10 terms will be:
|
|
||||||
// 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...
|
|
||||||
// By considering the terms in the Fibonacci sequence whose values do not exceed four million,
|
|
||||||
// find the sum of the even-valued terms.
|
|
||||||
//
|
|
||||||
// Correct Answer: 4613732
|
|
||||||
|
|
||||||
sum <- 0;
|
|
||||||
|
|
||||||
a <- 0;
|
|
||||||
b <- 1;
|
|
||||||
tmp <- 0;
|
|
||||||
|
|
||||||
loop a < 4_000_000 {
|
|
||||||
if a % 2 == 0 {
|
|
||||||
sum = sum + a;
|
|
||||||
}
|
|
||||||
|
|
||||||
tmp = a;
|
|
||||||
a = b;
|
|
||||||
b = b + tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
print sum;
|
|
||||||
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
// The prime factors of 13195 are 5, 7, 13 and 29.
|
|
||||||
// What is the largest prime factor of the number 600851475143 ?
|
|
||||||
//
|
|
||||||
// Correct Answer: 6857
|
|
||||||
|
|
||||||
number <- 600_851_475_143;
|
|
||||||
result <- 0;
|
|
||||||
|
|
||||||
div <- 2;
|
|
||||||
|
|
||||||
loop number > 1 {
|
|
||||||
loop number % div == 0 {
|
|
||||||
if div > result {
|
|
||||||
result = div;
|
|
||||||
}
|
|
||||||
number = number / div;
|
|
||||||
}
|
|
||||||
|
|
||||||
div = div + 1;
|
|
||||||
if div * div > number {
|
|
||||||
if number > 1 & number > result {
|
|
||||||
result = number;
|
|
||||||
}
|
|
||||||
number = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
print result;
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
// A palindromic number reads the same both ways. The largest palindrome made from the product of
|
|
||||||
// two 2-digit numbers is 9009 = 91 × 99.
|
|
||||||
// Find the largest palindrome made from the product of two 3-digit numbers.
|
|
||||||
//
|
|
||||||
// Correct Answer: 906609
|
|
||||||
|
|
||||||
|
|
||||||
res <- 0;
|
|
||||||
|
|
||||||
tmp <- 0;
|
|
||||||
num <- 0;
|
|
||||||
num_rev <- 0;
|
|
||||||
|
|
||||||
i <- 100;
|
|
||||||
k <- 100;
|
|
||||||
loop i < 1_000; i = i + 1 {
|
|
||||||
k = 100;
|
|
||||||
loop k < 1_000; k = k + 1 {
|
|
||||||
num_rev = 0;
|
|
||||||
|
|
||||||
num = i * k;
|
|
||||||
|
|
||||||
tmp = num;
|
|
||||||
|
|
||||||
loop tmp {
|
|
||||||
num_rev = num_rev*10 + tmp % 10;
|
|
||||||
tmp = tmp / 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
if num == num_rev & num > res {
|
|
||||||
res = num;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
print res;
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
# A palindromic number reads the same both ways. The largest palindrome made from the product of
|
|
||||||
# two 2-digit numbers is 9009 = 91 × 99.
|
|
||||||
# Find the largest palindrome made from the product of two 3-digit numbers.
|
|
||||||
#
|
|
||||||
# Correct Answer: 906609
|
|
||||||
|
|
||||||
|
|
||||||
res = 0
|
|
||||||
|
|
||||||
for i in range(100, 999):
|
|
||||||
for k in range(100, 999):
|
|
||||||
|
|
||||||
num = i * k
|
|
||||||
tmp = num
|
|
||||||
|
|
||||||
num_rev = 0
|
|
||||||
while tmp != 0:
|
|
||||||
num_rev = num_rev*10 + tmp % 10
|
|
||||||
tmp = tmp // 10
|
|
||||||
|
|
||||||
if num == num_rev and num > res:
|
|
||||||
res = num
|
|
||||||
|
|
||||||
print(res)
|
|
||||||
136
src/ast.rs
136
src/ast.rs
@@ -18,24 +18,6 @@ pub enum BinOpType {
|
|||||||
/// Modulo
|
/// Modulo
|
||||||
Mod,
|
Mod,
|
||||||
|
|
||||||
/// Compare Equal
|
|
||||||
EquEqu,
|
|
||||||
|
|
||||||
/// Compare Not Equal
|
|
||||||
NotEqu,
|
|
||||||
|
|
||||||
/// Less than
|
|
||||||
Less,
|
|
||||||
|
|
||||||
/// Less than or Equal
|
|
||||||
LessEqu,
|
|
||||||
|
|
||||||
/// Greater than
|
|
||||||
Greater,
|
|
||||||
|
|
||||||
/// Greater than or Equal
|
|
||||||
GreaterEqu,
|
|
||||||
|
|
||||||
/// Bitwise OR (inclusive or)
|
/// Bitwise OR (inclusive or)
|
||||||
BOr,
|
BOr,
|
||||||
|
|
||||||
@@ -45,84 +27,78 @@ pub enum BinOpType {
|
|||||||
/// Bitwise Xor (exclusive or)
|
/// Bitwise Xor (exclusive or)
|
||||||
BXor,
|
BXor,
|
||||||
|
|
||||||
/// Logical And
|
|
||||||
LAnd,
|
|
||||||
|
|
||||||
/// Logical Or
|
|
||||||
LOr,
|
|
||||||
|
|
||||||
/// Shift Left
|
/// Shift Left
|
||||||
Shl,
|
Shl,
|
||||||
|
|
||||||
/// Shift Right
|
/// Shift Right
|
||||||
Shr,
|
Shr,
|
||||||
|
|
||||||
/// Assign value to variable
|
/// Check equality
|
||||||
Assign,
|
Equ,
|
||||||
|
|
||||||
/// Declare new variable with value
|
/// Check unequality
|
||||||
Declare,
|
Neq,
|
||||||
|
|
||||||
|
/// Check greater than
|
||||||
|
Gt,
|
||||||
|
|
||||||
|
/// Check greater or equal
|
||||||
|
Ge,
|
||||||
|
|
||||||
|
/// Check less than
|
||||||
|
Lt,
|
||||||
|
|
||||||
|
/// Check less or equal
|
||||||
|
Le,
|
||||||
|
|
||||||
|
/// Assign to a variable
|
||||||
|
Assign,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Types for unary operators
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
pub enum UnOpType {
|
pub enum UnOpType {
|
||||||
/// Unary Negate
|
/// Negation
|
||||||
Negate,
|
Neg,
|
||||||
|
}
|
||||||
|
|
||||||
/// Bitwise Not
|
/// A full program abstract syntax tree. This consists of zero or more statements that represents
|
||||||
BNot,
|
/// a program.
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
/// Logical Not
|
pub struct Ast {
|
||||||
LNot,
|
pub prog: Vec<Stmt>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
pub enum Expression {
|
pub enum Stmt {
|
||||||
|
/// Just a simple expression. This might be an assignment, a function call or a calculation.
|
||||||
|
Expr(Expr),
|
||||||
|
/// A variable declaration and assignment. (variable name, assigned value)
|
||||||
|
Let(String, Expr),
|
||||||
|
/// A while loop consisting of a condition and a body. (condition, body)
|
||||||
|
While(Expr, Ast),
|
||||||
|
/// A for loop consisting of an initialization declaration, a condition, an advancement and a
|
||||||
|
/// body. ((variable name, initial value), condition, advancement, body)
|
||||||
|
For((String, Expr), Expr, Expr, Ast),
|
||||||
|
/// If statement consisting of a condition, a true_body and a false_body.
|
||||||
|
/// (condition, true_body, false_body)
|
||||||
|
If(Expr, Ast, Ast),
|
||||||
|
/// Debug print the value of an expression (show the internal type together with the value)
|
||||||
|
DbgPrint(Expr),
|
||||||
|
/// Print the value of an expression
|
||||||
|
Print(Expr),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
|
pub enum Expr {
|
||||||
/// Integer literal (64-bit)
|
/// Integer literal (64-bit)
|
||||||
I64(i64),
|
I64(i64),
|
||||||
/// String literal
|
/// String literal
|
||||||
String(Rc<String>),
|
Str(Rc<String>),
|
||||||
|
/// Identifier (variable name)
|
||||||
FunCall(String, Vec<Expression>),
|
Ident(String),
|
||||||
/// Variable
|
|
||||||
Var(String),
|
|
||||||
/// Binary operation. Consists of type, left hand side and right hand side
|
/// Binary operation. Consists of type, left hand side and right hand side
|
||||||
BinOp(BinOpType, Box<Expression>, Box<Expression>),
|
BinOp(BinOpType, Box<Expr>, Box<Expr>),
|
||||||
/// Unary operation. Consists of type and operand
|
/// Unary operation. Consists of type and the value that is operated on
|
||||||
UnOp(UnOpType, Box<Expression>),
|
UnOp(UnOpType, Box<Expr>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
|
||||||
pub struct Loop {
|
|
||||||
/// The condition that determines if the loop should continue
|
|
||||||
pub condition: Expression,
|
|
||||||
/// This is executed after each loop to advance the condition variables
|
|
||||||
pub advancement: Option<Expression>,
|
|
||||||
/// The loop body that is executed each loop
|
|
||||||
pub body: Ast,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
|
||||||
pub struct If {
|
|
||||||
/// The condition
|
|
||||||
pub condition: Expression,
|
|
||||||
/// The body that is executed when condition is true
|
|
||||||
pub body_true: Ast,
|
|
||||||
/// The if body that is executed when the condition is false
|
|
||||||
pub body_false: Ast,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
|
||||||
pub enum Statement {
|
|
||||||
Expr(Expression),
|
|
||||||
Loop(Loop),
|
|
||||||
If(If),
|
|
||||||
Print(Expression),
|
|
||||||
FunDecl(String, Vec<String>, Ast),
|
|
||||||
Return(Expression),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Default)]
|
|
||||||
pub struct Ast {
|
|
||||||
pub prog: Vec<Statement>,
|
|
||||||
}
|
|
||||||
211
src/bytecode.rs
Normal file
211
src/bytecode.rs
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use crate::ast::{Ast, Expr, Stmt, BinOpType};
|
||||||
|
|
||||||
|
type OpcodeSize = u32;
|
||||||
|
|
||||||
|
#[repr(u32)]
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub enum OP {
|
||||||
|
Push,
|
||||||
|
Pop,
|
||||||
|
Load,
|
||||||
|
Store,
|
||||||
|
|
||||||
|
Add,
|
||||||
|
Subtract,
|
||||||
|
Multiply,
|
||||||
|
Divide,
|
||||||
|
Modulo,
|
||||||
|
BOr,
|
||||||
|
BAnd,
|
||||||
|
BXor,
|
||||||
|
Shl,
|
||||||
|
Shr,
|
||||||
|
|
||||||
|
Eq,
|
||||||
|
Neq,
|
||||||
|
|
||||||
|
Gt,
|
||||||
|
Ge,
|
||||||
|
Lt,
|
||||||
|
Le,
|
||||||
|
|
||||||
|
Jump,
|
||||||
|
|
||||||
|
JumpTrue,
|
||||||
|
|
||||||
|
JumpFalse,
|
||||||
|
|
||||||
|
Print,
|
||||||
|
|
||||||
|
DbgPrint,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct Compiler {
|
||||||
|
ops: Vec<u32>,
|
||||||
|
global_vars: HashMap<String, u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Compiler {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Compiler::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compile(&mut self, ast: &Ast) {
|
||||||
|
for stmt in &ast.prog {
|
||||||
|
match stmt {
|
||||||
|
Stmt::Expr(expr) => {
|
||||||
|
self.compile_expr(expr);
|
||||||
|
self.ops.push(OP::Pop as OpcodeSize);
|
||||||
|
}
|
||||||
|
Stmt::Let(name, rhs) => {
|
||||||
|
let id = self.global_vars.len() as u64;
|
||||||
|
self.global_vars.insert(name.clone(), id);
|
||||||
|
|
||||||
|
self.compile_expr(rhs);
|
||||||
|
self.gen_store(id);
|
||||||
|
}
|
||||||
|
Stmt::While(cond, body) => {
|
||||||
|
let idx_start = self.ops.len();
|
||||||
|
self.compile_expr(cond);
|
||||||
|
|
||||||
|
self.ops.push(OP::JumpFalse as OpcodeSize);
|
||||||
|
let idx_jmp = self.ops.len();
|
||||||
|
self.gen_i64(0);
|
||||||
|
|
||||||
|
self.compile(body);
|
||||||
|
|
||||||
|
self.ops.push(OP::Jump as OpcodeSize);
|
||||||
|
self.gen_i64(idx_start as i64);
|
||||||
|
|
||||||
|
self.overwrite_i64(idx_jmp, self.ops.len() as i64);
|
||||||
|
}
|
||||||
|
Stmt::For(_, _, _, _) => todo!(),
|
||||||
|
Stmt::If(cond, if_block, else_block) => {
|
||||||
|
self.compile_expr(cond);
|
||||||
|
|
||||||
|
self.ops.push(OP::JumpFalse as OpcodeSize);
|
||||||
|
let idx_if = self.ops.len();
|
||||||
|
self.gen_i64(0);
|
||||||
|
|
||||||
|
self.compile(if_block);
|
||||||
|
|
||||||
|
self.ops.push(OP::Jump as OpcodeSize);
|
||||||
|
let idx_else = self.ops.len();
|
||||||
|
self.gen_i64(0);
|
||||||
|
|
||||||
|
self.overwrite_i64(idx_if, self.ops.len() as i64);
|
||||||
|
|
||||||
|
self.compile(else_block);
|
||||||
|
|
||||||
|
self.overwrite_i64(idx_else, self.ops.len() as i64);
|
||||||
|
|
||||||
|
},
|
||||||
|
Stmt::DbgPrint(expr) => {
|
||||||
|
self.compile_expr(expr);
|
||||||
|
self.ops.push(OP::DbgPrint as OpcodeSize);
|
||||||
|
}
|
||||||
|
Stmt::Print(expr) => {
|
||||||
|
self.compile_expr(expr);
|
||||||
|
self.ops.push(OP::Print as OpcodeSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_ops(self) -> Vec<u32> {
|
||||||
|
self.ops
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compile_expr(&mut self, expr: &Expr) {
|
||||||
|
match expr {
|
||||||
|
Expr::I64(val) => {
|
||||||
|
self.ops.push(OP::Push as OpcodeSize);
|
||||||
|
self.gen_i64(*val)
|
||||||
|
}
|
||||||
|
Expr::Ident(name) => {
|
||||||
|
match self.global_vars.get(name).copied() {
|
||||||
|
Some(addr) => self.gen_load(addr),
|
||||||
|
None => panic!("Variable '{}' used before declaration", name),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Expr::BinOp(bo, lhs, rhs) => self.compile_binop(bo, lhs, rhs),
|
||||||
|
Expr::UnOp(_, _) => todo!(),
|
||||||
|
Expr::Str(_) => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compile_binop(&mut self, bo: &BinOpType, lhs: &Expr, rhs: &Expr) {
|
||||||
|
|
||||||
|
if matches!(bo, BinOpType::Assign) {
|
||||||
|
self.compile_expr(rhs);
|
||||||
|
|
||||||
|
if let Expr::Ident(name) = lhs {
|
||||||
|
let addr = *self.global_vars.get(name).expect("Trying to assign var before decl");
|
||||||
|
self.gen_store(addr);
|
||||||
|
} else {
|
||||||
|
panic!("Trying to assign value to rvalue");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.compile_expr(lhs);
|
||||||
|
self.compile_expr(rhs);
|
||||||
|
|
||||||
|
match bo {
|
||||||
|
BinOpType::Add => self.ops.push(OP::Add as OpcodeSize),
|
||||||
|
BinOpType::Sub => self.ops.push(OP::Subtract as OpcodeSize),
|
||||||
|
BinOpType::Mul => self.ops.push(OP::Multiply as OpcodeSize),
|
||||||
|
BinOpType::Div => self.ops.push(OP::Divide as OpcodeSize),
|
||||||
|
BinOpType::Mod => self.ops.push(OP::Modulo as OpcodeSize),
|
||||||
|
BinOpType::BOr => self.ops.push(OP::BOr as OpcodeSize),
|
||||||
|
BinOpType::BAnd => self.ops.push(OP::BAnd as OpcodeSize),
|
||||||
|
BinOpType::BXor => self.ops.push(OP::BXor as OpcodeSize),
|
||||||
|
BinOpType::Shl => self.ops.push(OP::Shl as OpcodeSize),
|
||||||
|
BinOpType::Shr => self.ops.push(OP::Shr as OpcodeSize),
|
||||||
|
BinOpType::Equ => self.ops.push(OP::Eq as OpcodeSize),
|
||||||
|
BinOpType::Neq => self.ops.push(OP::Neq as OpcodeSize),
|
||||||
|
BinOpType::Gt => self.ops.push(OP::Gt as OpcodeSize),
|
||||||
|
BinOpType::Ge => self.ops.push(OP::Ge as OpcodeSize),
|
||||||
|
BinOpType::Lt => self.ops.push(OP::Lt as OpcodeSize),
|
||||||
|
BinOpType::Le => self.ops.push(OP::Le as OpcodeSize),
|
||||||
|
BinOpType::Assign => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gen_i64(&mut self, val: i64) {
|
||||||
|
// for i in 0 .. 8 {
|
||||||
|
// self.ops.push(((val >> i*8) & 0xff) as OpcodeSize);
|
||||||
|
// }
|
||||||
|
for i in 0 .. 2 {
|
||||||
|
self.ops.push(((val >> i*32) & 0xffffffff) as OpcodeSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn overwrite_i64(&mut self, idx: usize, val: i64) {
|
||||||
|
// for i in 0 .. 8 {
|
||||||
|
// self.ops[idx+i] = ((val >> i*8) & 0xff) as OpcodeSize;
|
||||||
|
// }
|
||||||
|
for i in 0 .. 2 {
|
||||||
|
self.ops[idx+i] = ((val >> i*32) & 0xffffffff) as OpcodeSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gen_load(&mut self, addr: u64) {
|
||||||
|
self.ops.push(OP::Load as OpcodeSize);
|
||||||
|
self.gen_i64(addr as i64)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gen_store(&mut self, addr: u64) {
|
||||||
|
self.ops.push(OP::Store as OpcodeSize);
|
||||||
|
self.gen_i64(addr as i64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compile(ast: &Ast) -> Vec<u32> {
|
||||||
|
let mut compiler = Compiler::new();
|
||||||
|
compiler.compile(ast);
|
||||||
|
compiler.into_ops()
|
||||||
|
}
|
||||||
@@ -1,164 +1,134 @@
|
|||||||
use std::{collections::HashMap, fmt::Display, rc::Rc, cell::RefCell};
|
use std::{collections::HashMap, fmt::Display, rc::Rc};
|
||||||
|
|
||||||
use crate::{ast::{Expression, BinOpType, UnOpType, Ast, Statement, If}, parser::parse, lexer::lex};
|
use crate::{
|
||||||
|
ast::{Ast, BinOpType, Expr, Stmt, UnOpType},
|
||||||
|
lexer::lex,
|
||||||
|
parser::parse,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
I64(i64),
|
I64(i64),
|
||||||
String(Rc<String>),
|
Str(Rc<String>),
|
||||||
}
|
|
||||||
|
|
||||||
pub enum RunEnd {
|
|
||||||
Return(Value),
|
|
||||||
End,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Interpreter {
|
pub struct Interpreter {
|
||||||
// Variable table stores the runtime values of variables
|
/// The variable table maps all variables by their names to their values
|
||||||
vartable: HashMap<String, Value>,
|
vartable: HashMap<String, Value>,
|
||||||
funtable: HashMap<String, RefCell<(Vec<String>, Ast)>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Interpreter {
|
impl Interpreter {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
let vartable = HashMap::new();
|
||||||
vartable: HashMap::new(),
|
Self { vartable }
|
||||||
funtable: HashMap::new(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_str(&mut self, code: &str, print_tokens: bool, print_ast: bool) {
|
pub fn run_text(&mut self, code: &str, print_tokens: bool, print_ast: bool) {
|
||||||
let tokens = lex(code).unwrap();
|
let tokens = lex(code);
|
||||||
if print_tokens {
|
if print_tokens {
|
||||||
println!("Tokens: {:?}", tokens);
|
println!("Tokens: {:?}", tokens);
|
||||||
}
|
}
|
||||||
|
|
||||||
let ast = parse(tokens);
|
let ast = parse(tokens);
|
||||||
if print_ast {
|
if print_ast {
|
||||||
println!("{:#?}", ast);
|
println!("Ast:\n{:#?}", ast);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.run(&ast);
|
self.run(&ast);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(&mut self, prog: &Ast) -> RunEnd {
|
pub fn run(&mut self, prog: &Ast) {
|
||||||
for stmt in &prog.prog {
|
for stmt in &prog.prog {
|
||||||
match stmt {
|
match stmt {
|
||||||
Statement::Expr(expr) => {
|
Stmt::Expr(expr) => {
|
||||||
self.resolve_expr(expr);
|
self.resolve_expr(expr);
|
||||||
}
|
}
|
||||||
|
Stmt::DbgPrint(expr) => {
|
||||||
Statement::Return(expr) => {
|
let result = self.resolve_expr(expr);
|
||||||
return RunEnd::Return(self.resolve_expr(expr));
|
println!("{:?}", result);
|
||||||
}
|
}
|
||||||
|
Stmt::Print(expr) => {
|
||||||
Statement::Loop(looop) => {
|
|
||||||
// loop runs as long condition != 0
|
|
||||||
loop {
|
|
||||||
if matches!(self.resolve_expr(&looop.condition), Value::I64(0)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
match self.run(&looop.body) {
|
|
||||||
RunEnd::Return(val) => return RunEnd::Return(val),
|
|
||||||
RunEnd::End => (),
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(adv) = &looop.advancement {
|
|
||||||
self.resolve_expr(&adv);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Statement::Print(expr) => {
|
|
||||||
let result = self.resolve_expr(expr);
|
let result = self.resolve_expr(expr);
|
||||||
print!("{}", result);
|
print!("{}", result);
|
||||||
}
|
}
|
||||||
|
Stmt::Let(name, rhs) => {
|
||||||
|
let result = self.resolve_expr(rhs);
|
||||||
|
self.vartable.insert(name.clone(), result);
|
||||||
|
}
|
||||||
|
Stmt::For(init, condition, advance, body) => {
|
||||||
|
// Execute initital let instruction
|
||||||
|
let init_val = self.resolve_expr(&init.1);
|
||||||
|
self.vartable.insert(init.0.clone(), init_val);
|
||||||
|
|
||||||
Statement::If(If {condition, body_true, body_false}) => {
|
loop {
|
||||||
let end = if matches!(self.resolve_expr(condition), Value::I64(0)) {
|
// Check condition
|
||||||
self.run(body_false)
|
match self.resolve_expr(condition) {
|
||||||
} else {
|
Value::I64(val) if val == 0 => break,
|
||||||
self.run(body_true)
|
Value::I64(_) => (),
|
||||||
};
|
|
||||||
match end {
|
Value::Str(text) if text.is_empty() => break,
|
||||||
RunEnd::Return(val) => return RunEnd::Return(val),
|
Value::Str(_) => (),
|
||||||
RunEnd::End => (),
|
}
|
||||||
|
|
||||||
|
// Execute loop body
|
||||||
|
self.run(body);
|
||||||
|
|
||||||
|
// Execute advancement
|
||||||
|
self.resolve_expr(advance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Statement::FunDecl(name, args, body) => {
|
Stmt::While(condition, body) => {
|
||||||
self.funtable.insert(name.clone(), (args.clone(), body.clone()).into());
|
loop {
|
||||||
|
// Check condition
|
||||||
|
match self.resolve_expr(condition) {
|
||||||
|
Value::I64(val) if val == 0 => break,
|
||||||
|
Value::I64(_) => (),
|
||||||
|
|
||||||
|
Value::Str(text) if text.is_empty() => break,
|
||||||
|
Value::Str(_) => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute loop body
|
||||||
|
self.run(body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Stmt::If(condition, body_if, body_else) => {
|
||||||
|
if matches!(self.resolve_expr(condition), Value::I64(0)) {
|
||||||
|
self.run(body_else);
|
||||||
|
} else {
|
||||||
|
self.run(body_if);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RunEnd::End
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_expr(&mut self, expr: &Expression) -> Value {
|
fn resolve_expr(&mut self, expr: &Expr) -> Value {
|
||||||
match expr {
|
match expr {
|
||||||
Expression::I64(val) => Value::I64(*val),
|
Expr::I64(val) => Value::I64(*val),
|
||||||
Expression::String(text) => Value::String(text.clone()),
|
Expr::Str(name) => Value::Str(name.clone()),
|
||||||
Expression::BinOp(bo, lhs, rhs) => self.resolve_binop(bo, lhs, rhs),
|
Expr::BinOp(bo, lhs, rhs) => self.resolve_binop(bo, &lhs, &rhs),
|
||||||
Expression::UnOp(uo, operand) => self.resolve_unop(uo, operand),
|
Expr::UnOp(uo, val) => self.resolve_unop(uo, &val),
|
||||||
Expression::Var(name) => self.resolve_var(name),
|
Expr::Ident(name) => match self.vartable.get(name) {
|
||||||
Expression::FunCall(name, args) => {
|
None => panic!("Runtime error: Use of undeclared variable '{}'", name),
|
||||||
let fun = self.funtable.get(name).expect("Function not declared").clone();
|
Some(val) => val.clone(),
|
||||||
for i in 0 .. args.len() {
|
},
|
||||||
let val = self.resolve_expr(&args[i]);
|
|
||||||
self.vartable.insert(fun.borrow().0[i].clone(), val);
|
|
||||||
}
|
|
||||||
|
|
||||||
if fun.borrow().0.len() != args.len() {
|
|
||||||
panic!("Invalid number of arguments for function");
|
|
||||||
}
|
|
||||||
|
|
||||||
let end = self.run(&fun.borrow().1);
|
|
||||||
match end {
|
|
||||||
RunEnd::Return(val) => val,
|
|
||||||
RunEnd::End => Value::I64(0),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_var(&mut self, name: &str) -> Value {
|
fn resolve_binop(&mut self, bo: &BinOpType, lhs: &Expr, rhs: &Expr) -> Value {
|
||||||
match self.vartable.get(name) {
|
// Treat assignment separate from the other expressions
|
||||||
Some(val) => val.clone(),
|
if matches!(bo, BinOpType::Assign) {
|
||||||
None => panic!("Variable '{}' used but not declared", name),
|
match lhs {
|
||||||
}
|
Expr::Ident(name) => {
|
||||||
}
|
let rhs = self.resolve_expr(rhs);
|
||||||
|
self.vartable.get_mut(name).map(|var| *var = rhs.clone());
|
||||||
fn resolve_unop(&mut self, uo: &UnOpType, operand: &Expression) -> Value {
|
return rhs;
|
||||||
let operand = self.resolve_expr(operand);
|
|
||||||
|
|
||||||
match (operand, uo) {
|
|
||||||
(Value::I64(val), UnOpType::Negate) => Value::I64(-val),
|
|
||||||
(Value::I64(val), UnOpType::BNot) => Value::I64(!val),
|
|
||||||
(Value::I64(val), UnOpType::LNot) => Value::I64(if val == 0 { 1 } else { 0 }),
|
|
||||||
_ => panic!("Value type is not compatible with unary operation"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resolve_binop(&mut self, bo: &BinOpType, lhs: &Expression, rhs: &Expression) -> Value {
|
|
||||||
let rhs = self.resolve_expr(rhs);
|
|
||||||
|
|
||||||
match (&bo, &lhs) {
|
|
||||||
(BinOpType::Declare, Expression::Var(name)) => {
|
|
||||||
self.vartable.insert(name.clone(), rhs.clone());
|
|
||||||
return rhs;
|
|
||||||
}
|
|
||||||
(BinOpType::Assign, Expression::Var(name)) => {
|
|
||||||
match self.vartable.get_mut(name) {
|
|
||||||
Some(val) => *val = rhs.clone(),
|
|
||||||
None => panic!("Runtime Error: Trying to assign value to undeclared variable"),
|
|
||||||
}
|
}
|
||||||
return rhs;
|
_ => panic!("Runtime error: Left hand side of assignment must be an identifier"),
|
||||||
}
|
}
|
||||||
_ => ()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let lhs = self.resolve_expr(lhs);
|
let lhs = self.resolve_expr(lhs);
|
||||||
|
let rhs = self.resolve_expr(rhs);
|
||||||
|
|
||||||
match (lhs, rhs) {
|
match (lhs, rhs) {
|
||||||
(Value::I64(lhs), Value::I64(rhs)) => match bo {
|
(Value::I64(lhs), Value::I64(rhs)) => match bo {
|
||||||
@@ -170,52 +140,58 @@ impl Interpreter {
|
|||||||
BinOpType::BOr => Value::I64(lhs | rhs),
|
BinOpType::BOr => Value::I64(lhs | rhs),
|
||||||
BinOpType::BAnd => Value::I64(lhs & rhs),
|
BinOpType::BAnd => Value::I64(lhs & rhs),
|
||||||
BinOpType::BXor => Value::I64(lhs ^ rhs),
|
BinOpType::BXor => Value::I64(lhs ^ rhs),
|
||||||
BinOpType::LAnd => Value::I64(if (lhs != 0) && (rhs != 0) { 1 } else { 0 }),
|
|
||||||
BinOpType::LOr => Value::I64(if (lhs != 0) || (rhs != 0) { 1 } else { 0 }),
|
|
||||||
BinOpType::Shr => Value::I64(lhs >> rhs),
|
BinOpType::Shr => Value::I64(lhs >> rhs),
|
||||||
BinOpType::Shl => Value::I64(lhs << rhs),
|
BinOpType::Shl => Value::I64(lhs << rhs),
|
||||||
BinOpType::EquEqu => Value::I64(if lhs == rhs { 1 } else { 0 }),
|
BinOpType::Equ => Value::I64(if lhs == rhs { 1 } else { 0 }),
|
||||||
BinOpType::NotEqu => Value::I64(if lhs != rhs { 1 } else { 0 }),
|
BinOpType::Neq => Value::I64(if lhs != rhs { 1 } else { 0 }),
|
||||||
BinOpType::Less => Value::I64(if lhs < rhs { 1 } else { 0 }),
|
BinOpType::Gt => Value::I64(if lhs > rhs { 1 } else { 0 }),
|
||||||
BinOpType::LessEqu => Value::I64(if lhs <= rhs { 1 } else { 0 }),
|
BinOpType::Ge => Value::I64(if lhs >= rhs { 1 } else { 0 }),
|
||||||
BinOpType::Greater => Value::I64(if lhs > rhs { 1 } else { 0 }),
|
BinOpType::Lt => Value::I64(if lhs < rhs { 1 } else { 0 }),
|
||||||
BinOpType::GreaterEqu => Value::I64(if lhs >= rhs { 1 } else { 0 }),
|
BinOpType::Le => Value::I64(if lhs <= rhs { 1 } else { 0 }),
|
||||||
|
BinOpType::Assign => unreachable!(),
|
||||||
BinOpType::Declare | BinOpType::Assign => unreachable!(),
|
|
||||||
},
|
},
|
||||||
_ => panic!("Value types are not compatible"),
|
_ => panic!("Value types are not compatible"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn resolve_unop(&mut self, uo: &UnOpType, val: &Expr) -> Value {
|
||||||
|
let val = self.resolve_expr(val);
|
||||||
|
match val {
|
||||||
|
Value::I64(val) => match uo {
|
||||||
|
UnOpType::Neg => Value::I64(-val),
|
||||||
|
},
|
||||||
|
_ => panic!("Invalid unary operation for type"),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Value {
|
impl Display for Value {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Value::I64(val) => write!(f, "{}", val),
|
Value::I64(val) => write!(f, "{}", val),
|
||||||
Value::String(text) => write!(f, "{}", text),
|
Value::Str(text) => write!(f, "{}", text),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::{Interpreter, Value};
|
use super::{Interpreter, Value};
|
||||||
use crate::ast::{Expression, BinOpType};
|
use crate::ast::{BinOpType, Expr};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_interpreter_expr() {
|
fn test_interpreter_expr() {
|
||||||
// Expression: 1 + 2 * 3 + 4
|
// Expression: 1 + 2 * 3 + 4
|
||||||
// With precedence: (1 + (2 * 3)) + 4
|
// With precedence: (1 + (2 * 3)) + 4
|
||||||
let ast = Expression::BinOp(
|
let ast = Expr::BinOp(
|
||||||
BinOpType::Add,
|
BinOpType::Add,
|
||||||
Expression::BinOp(
|
Expr::BinOp(
|
||||||
BinOpType::Add,
|
BinOpType::Add,
|
||||||
Expression::I64(1).into(),
|
Expr::I64(1).into(),
|
||||||
Expression::BinOp(BinOpType::Mul, Expression::I64(2).into(), Expression::I64(3).into()).into(),
|
Expr::BinOp(BinOpType::Mul, Expr::I64(2).into(), Expr::I64(3).into()).into(),
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
Expression::I64(4).into(),
|
Expr::I64(4).into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let expected = Value::I64(11);
|
let expected = Value::I64(11);
|
||||||
|
|||||||
291
src/lexer.rs
291
src/lexer.rs
@@ -1,31 +1,14 @@
|
|||||||
use crate::token::Token;
|
|
||||||
use anyhow::Result;
|
|
||||||
use std::{iter::Peekable, str::Chars};
|
use std::{iter::Peekable, str::Chars};
|
||||||
use thiserror::Error;
|
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
use crate::token::{Keyword, Literal, Token};
|
||||||
pub enum LexErr {
|
|
||||||
#[error("Failed to parse '{0}' as i64")]
|
|
||||||
NumericParse(String),
|
|
||||||
|
|
||||||
#[error("Invalid escape character '\\{0}'")]
|
|
||||||
InvalidStrEscape(char),
|
|
||||||
|
|
||||||
#[error("Lexer encountered unexpected char: '{0}'")]
|
|
||||||
UnexpectedChar(char),
|
|
||||||
|
|
||||||
#[error("Missing closing string quote '\"'")]
|
|
||||||
MissingClosingString,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Lex the provided code into a Token Buffer
|
/// Lex the provided code into a Token Buffer
|
||||||
pub fn lex(code: &str) -> Result<Vec<Token>, LexErr> {
|
pub fn lex(code: &str) -> Vec<Token> {
|
||||||
let mut lexer = Lexer::new(code);
|
let mut lexer = Lexer::new(code);
|
||||||
lexer.lex()
|
lexer.lex()
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Lexer<'a> {
|
struct Lexer<'a> {
|
||||||
/// The sourcecode text as an iterator over the chars
|
|
||||||
code: Peekable<Chars<'a>>,
|
code: Peekable<Chars<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,21 +18,29 @@ impl<'a> Lexer<'a> {
|
|||||||
Self { code }
|
Self { code }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lex(&mut self) -> Result<Vec<Token>, LexErr> {
|
/// Advance to next character and return the removed char. If there is no next char, '\0'
|
||||||
|
/// is returned.
|
||||||
|
fn next(&mut self) -> char {
|
||||||
|
self.code.next().unwrap_or('\0')
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the next character without removing it. If there is no next char, '\0' is returned.
|
||||||
|
fn peek(&mut self) -> char {
|
||||||
|
self.code.peek().copied().unwrap_or('\0')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lex(&mut self) -> Vec<Token> {
|
||||||
let mut tokens = Vec::new();
|
let mut tokens = Vec::new();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
match self.next() {
|
match self.next() {
|
||||||
// Stop lexing at EOF
|
// End of text
|
||||||
'\0' => break,
|
'\0' => break,
|
||||||
|
|
||||||
// Skip whitespace
|
// Skip whitespace
|
||||||
' ' | '\t' | '\n' | '\r' => (),
|
' ' | '\r' | '\n' | '\t' => (),
|
||||||
|
|
||||||
// Line comment. Consume every char until linefeed (next line)
|
// Handle tokens that span two characters
|
||||||
'/' if matches!(self.peek(), '/') => while !matches!(self.next(), '\n' | '\0') {},
|
|
||||||
|
|
||||||
// Double character tokens
|
|
||||||
'>' if matches!(self.peek(), '>') => {
|
'>' if matches!(self.peek(), '>') => {
|
||||||
self.next();
|
self.next();
|
||||||
tokens.push(Token::Shr);
|
tokens.push(Token::Shr);
|
||||||
@@ -60,35 +51,26 @@ impl<'a> Lexer<'a> {
|
|||||||
}
|
}
|
||||||
'=' if matches!(self.peek(), '=') => {
|
'=' if matches!(self.peek(), '=') => {
|
||||||
self.next();
|
self.next();
|
||||||
tokens.push(Token::EquEqu);
|
tokens.push(Token::Equ);
|
||||||
}
|
}
|
||||||
'!' if matches!(self.peek(), '=') => {
|
'!' if matches!(self.peek(), '=') => {
|
||||||
self.next();
|
self.next();
|
||||||
tokens.push(Token::NotEqu);
|
tokens.push(Token::Neq);
|
||||||
}
|
}
|
||||||
'<' if matches!(self.peek(), '=') => {
|
'<' if matches!(self.peek(), '=') => {
|
||||||
self.next();
|
self.next();
|
||||||
tokens.push(Token::LAngleEqu);
|
tokens.push(Token::Le);
|
||||||
}
|
}
|
||||||
'>' if matches!(self.peek(), '=') => {
|
'>' if matches!(self.peek(), '=') => {
|
||||||
self.next();
|
self.next();
|
||||||
tokens.push(Token::RAngleEqu);
|
tokens.push(Token::Ge);
|
||||||
}
|
}
|
||||||
'<' if matches!(self.peek(), '-') => {
|
'$' if matches!(self.peek(), '$') => {
|
||||||
self.next();
|
self.next();
|
||||||
tokens.push(Token::LArrow);
|
tokens.push(Token::DoubleDollar);
|
||||||
}
|
|
||||||
'&' if matches!(self.peek(), '&') => {
|
|
||||||
self.next();
|
|
||||||
tokens.push(Token::LAnd);
|
|
||||||
}
|
|
||||||
'|' if matches!(self.peek(), '|') => {
|
|
||||||
self.next();
|
|
||||||
tokens.push(Token::LOr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Single character tokens
|
// Handle tokens that span one character
|
||||||
';' => tokens.push(Token::Semicolon),
|
|
||||||
'+' => tokens.push(Token::Add),
|
'+' => tokens.push(Token::Add),
|
||||||
'-' => tokens.push(Token::Sub),
|
'-' => tokens.push(Token::Sub),
|
||||||
'*' => tokens.push(Token::Mul),
|
'*' => tokens.push(Token::Mul),
|
||||||
@@ -99,144 +81,147 @@ impl<'a> Lexer<'a> {
|
|||||||
'^' => tokens.push(Token::BXor),
|
'^' => tokens.push(Token::BXor),
|
||||||
'(' => tokens.push(Token::LParen),
|
'(' => tokens.push(Token::LParen),
|
||||||
')' => tokens.push(Token::RParen),
|
')' => tokens.push(Token::RParen),
|
||||||
'~' => tokens.push(Token::Tilde),
|
'<' => tokens.push(Token::Lt),
|
||||||
'<' => tokens.push(Token::LAngle),
|
'>' => tokens.push(Token::Gt),
|
||||||
'>' => tokens.push(Token::RAngle),
|
'=' => tokens.push(Token::Assign),
|
||||||
'=' => tokens.push(Token::Equ),
|
';' => tokens.push(Token::Semicolon),
|
||||||
'{' => tokens.push(Token::LBraces),
|
'{' => tokens.push(Token::LBrace),
|
||||||
'}' => tokens.push(Token::RBraces),
|
'}' => tokens.push(Token::RBrace),
|
||||||
'!' => tokens.push(Token::LNot),
|
'$' => tokens.push(Token::Dollar),
|
||||||
',' => tokens.push(Token::Comma),
|
|
||||||
|
// Handle special multicharacter tokens
|
||||||
|
|
||||||
// Lex numbers
|
// Lex numbers
|
||||||
ch @ '0'..='9' => {
|
ch @ '0'..='9' => tokens.push(self.lex_number(ch)),
|
||||||
// String representation of the integer value
|
|
||||||
let mut sval = String::from(ch);
|
|
||||||
|
|
||||||
// Do as long as a next char exists and it is a numeric char
|
// Lex strings
|
||||||
loop {
|
'"' => tokens.push(self.lex_string()),
|
||||||
// The next char is verified to be Some, so unwrap is safe
|
|
||||||
match self.peek() {
|
|
||||||
// Underscore is a separator, so remove it but don't add to number
|
|
||||||
'_' => {
|
|
||||||
self.next();
|
|
||||||
}
|
|
||||||
'0'..='9' => {
|
|
||||||
sval.push(self.next());
|
|
||||||
}
|
|
||||||
// Next char is not a number, so stop and finish the number token
|
|
||||||
_ => break,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to convert the string representation of the value to i64
|
// Lex identifiers
|
||||||
let i64val = sval.parse().map_err(|_| LexErr::NumericParse(sval))?;
|
ch @ ('a'..='z' | 'A'..='Z' | '_') => tokens.push(self.lex_ident(ch)),
|
||||||
tokens.push(Token::I64(i64val));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lex a string
|
// Any other character is unexpected
|
||||||
'"' => {
|
ch => panic!("Lexer encountered unexpected char: '{}'", ch),
|
||||||
// Opening " was consumed in match
|
|
||||||
|
|
||||||
let mut text = String::new();
|
|
||||||
|
|
||||||
// Read all chars until encountering the closing "
|
|
||||||
loop {
|
|
||||||
match self.peek() {
|
|
||||||
'"' => break,
|
|
||||||
// If the end of file is reached while still waiting for '"', error out
|
|
||||||
'\0' => Err(LexErr::MissingClosingString)?,
|
|
||||||
_ => match self.next() {
|
|
||||||
// Backshlash indicates an escaped character
|
|
||||||
'\\' => match self.next() {
|
|
||||||
'n' => text.push('\n'),
|
|
||||||
'r' => text.push('\r'),
|
|
||||||
't' => text.push('\t'),
|
|
||||||
'\\' => text.push('\\'),
|
|
||||||
'"' => text.push('"'),
|
|
||||||
ch => Err(LexErr::InvalidStrEscape(ch))?,
|
|
||||||
},
|
|
||||||
// All other characters are simply appended to the string
|
|
||||||
ch => text.push(ch),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Consume closing "
|
|
||||||
self.next();
|
|
||||||
|
|
||||||
tokens.push(Token::String(text))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lex characters as identifier
|
|
||||||
ch @ ('a'..='z' | 'A'..='Z' | '_') => {
|
|
||||||
let mut ident = String::from(ch);
|
|
||||||
|
|
||||||
// Do as long as a next char exists and it is a valid char for an identifier
|
|
||||||
loop {
|
|
||||||
match self.peek() {
|
|
||||||
// In the middle of an identifier numbers are also allowed
|
|
||||||
'a'..='z' | 'A'..='Z' | '0'..='9' | '_' => {
|
|
||||||
ident.push(self.next());
|
|
||||||
}
|
|
||||||
// Next char is not valid, so stop and finish the ident token
|
|
||||||
_ => break,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for pre-defined keywords
|
|
||||||
let token = match ident.as_str() {
|
|
||||||
"loop" => Token::Loop,
|
|
||||||
"print" => Token::Print,
|
|
||||||
"if" => Token::If,
|
|
||||||
"else" => Token::Else,
|
|
||||||
"fun" => Token::Fun,
|
|
||||||
"return" => Token::Return,
|
|
||||||
|
|
||||||
// If it doesn't match a keyword, it is a normal identifier
|
|
||||||
_ => Token::Ident(ident),
|
|
||||||
};
|
|
||||||
|
|
||||||
tokens.push(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
ch => Err(LexErr::UnexpectedChar(ch))?,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(tokens)
|
tokens
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Advance to next character and return the removed char
|
fn lex_number(&mut self, first_char: char) -> Token {
|
||||||
fn next(&mut self) -> char {
|
let mut sval = String::from(first_char);
|
||||||
self.code.next().unwrap_or('\0')
|
|
||||||
|
// Do as long as a next char exists and it is a numeric char
|
||||||
|
loop {
|
||||||
|
// The next char is verified to be Some, so unwrap is safe
|
||||||
|
match self.peek() {
|
||||||
|
// Underscore is a separator, so remove it but don't add to number
|
||||||
|
'_' => {
|
||||||
|
self.next();
|
||||||
|
}
|
||||||
|
'0'..='9' => {
|
||||||
|
sval.push(self.next());
|
||||||
|
}
|
||||||
|
// Next char is not a number, so stop and finish the number token
|
||||||
|
_ => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: We only added numeric chars to the string, but the conversion could still fail
|
||||||
|
Token::Literal(Literal::I64(sval.parse().unwrap()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the next character without removing it
|
/// Lex an identifier from the character stream. The first char has to have been consumed
|
||||||
fn peek(&mut self) -> char {
|
/// from the stream already and is passed as an argument instead.
|
||||||
self.code.peek().copied().unwrap_or('\0')
|
fn lex_ident(&mut self, first_char: char) -> Token {
|
||||||
|
let mut ident = String::from(first_char);
|
||||||
|
|
||||||
|
// Do as long as a next char exists and it is a valid ident char
|
||||||
|
while let 'a'..='z' | 'A'..='Z' | '_' | '0'..='9' = self.peek() {
|
||||||
|
// The next char is verified to be Some, so unwrap is safe
|
||||||
|
ident.push(self.next());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the identifier is a keyword
|
||||||
|
match ident.as_str() {
|
||||||
|
"true" => Token::Literal(Literal::I64(1)),
|
||||||
|
"false" => Token::Literal(Literal::I64(0)),
|
||||||
|
"let" => Token::Keyword(Keyword::Let),
|
||||||
|
"while" => Token::Keyword(Keyword::While),
|
||||||
|
"if" => Token::Keyword(Keyword::If),
|
||||||
|
"else" => Token::Keyword(Keyword::Else),
|
||||||
|
"for" => Token::Keyword(Keyword::For),
|
||||||
|
|
||||||
|
_ => Token::Ident(ident),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Lex a string token from the character stream. This requires the initial quote '"' to be
|
||||||
|
/// consumed before.
|
||||||
|
fn lex_string(&mut self) -> Token {
|
||||||
|
let mut text = String::new();
|
||||||
|
|
||||||
|
let mut escape = false;
|
||||||
|
|
||||||
|
// Do as long as a next char exists and it is not '"'
|
||||||
|
loop {
|
||||||
|
if escape {
|
||||||
|
escape = false;
|
||||||
|
|
||||||
|
// Escape characters
|
||||||
|
match self.next() {
|
||||||
|
'\\' => text.push('\\'),
|
||||||
|
'n' => text.push('\n'),
|
||||||
|
'r' => text.push('\r'),
|
||||||
|
't' => text.push('\t'),
|
||||||
|
ch => panic!("Invalid string escape: '{:?}'", ch),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match self.peek() {
|
||||||
|
// Doublequote '"' ends the string lexing
|
||||||
|
'"' => {
|
||||||
|
self.next();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Backslash '\' escapes the next character
|
||||||
|
'\\' => {
|
||||||
|
self.next();
|
||||||
|
escape = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reached end of text but didn't encounter closing doublequote '"'
|
||||||
|
'\0' => panic!("String is never terminated (missing '\"')"),
|
||||||
|
|
||||||
|
_ => text.push(self.next()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Token::Literal(Literal::Str(text))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use crate::token::Literal;
|
||||||
|
|
||||||
use super::{lex, Token};
|
use super::{lex, Token};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_lexer() {
|
fn test_lexer() {
|
||||||
let code = "33 +5*2 + 4456467*2334+3 % - / << ^ | & >>";
|
let code = "33 +5*2 + 4456467*2334+3 % - / << ^ | & >>";
|
||||||
let expected = vec![
|
let expected = vec![
|
||||||
Token::I64(33),
|
Token::Literal(Literal::I64(33)),
|
||||||
Token::Add,
|
Token::Add,
|
||||||
Token::I64(5),
|
Token::Literal(Literal::I64(5)),
|
||||||
Token::Mul,
|
Token::Mul,
|
||||||
Token::I64(2),
|
Token::Literal(Literal::I64(2)),
|
||||||
Token::Add,
|
Token::Add,
|
||||||
Token::I64(4456467),
|
Token::Literal(Literal::I64(4456467)),
|
||||||
Token::Mul,
|
Token::Mul,
|
||||||
Token::I64(2334),
|
Token::Literal(Literal::I64(2334)),
|
||||||
Token::Add,
|
Token::Add,
|
||||||
Token::I64(3),
|
Token::Literal(Literal::I64(3)),
|
||||||
Token::Mod,
|
Token::Mod,
|
||||||
Token::Sub,
|
Token::Sub,
|
||||||
Token::Div,
|
Token::Div,
|
||||||
@@ -247,7 +232,7 @@ mod tests {
|
|||||||
Token::Shr,
|
Token::Shr,
|
||||||
];
|
];
|
||||||
|
|
||||||
let actual = lex(code).unwrap();
|
let actual = lex(code);
|
||||||
assert_eq!(expected, actual);
|
assert_eq!(expected, actual);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
pub mod lexer;
|
pub mod lexer;
|
||||||
pub mod token;
|
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
pub mod ast;
|
|
||||||
pub mod interpreter;
|
pub mod interpreter;
|
||||||
|
pub mod token;
|
||||||
|
pub mod ast;
|
||||||
|
pub mod bytecode;
|
||||||
|
pub mod vm;
|
||||||
|
|||||||
50
src/main.rs
50
src/main.rs
@@ -1,7 +1,6 @@
|
|||||||
use std::{env::args, fs, io::{stdout, Write, stdin}};
|
use std::{env::args, io::Write};
|
||||||
|
|
||||||
use nek_lang::interpreter::Interpreter;
|
|
||||||
|
|
||||||
|
use nek_lang::{interpreter::Interpreter, lexer::lex, parser::parse, bytecode::compile, vm::Vm};
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
struct CliConfig {
|
struct CliConfig {
|
||||||
@@ -12,44 +11,53 @@ struct CliConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
let mut cfg = CliConfig::default();
|
||||||
|
|
||||||
let mut conf = CliConfig::default();
|
|
||||||
|
|
||||||
// Go through all commandline arguments except the first (filename)
|
|
||||||
for arg in args().skip(1) {
|
for arg in args().skip(1) {
|
||||||
match arg.as_str() {
|
match arg.as_str() {
|
||||||
"--token" | "-t" => conf.print_tokens = true,
|
"--tokens" | "-t" => cfg.print_tokens = true,
|
||||||
"--ast" | "-a" => conf.print_ast = true,
|
"--ast" | "-a" => cfg.print_ast = true,
|
||||||
"--interactive" | "-i" => conf.interactive = true,
|
"--interactive" | "-i" => cfg.interactive = true,
|
||||||
file if conf.file.is_none() => conf.file = Some(file.to_string()),
|
file if cfg.file.is_none() => cfg.file = Some(file.to_string()),
|
||||||
_ => panic!("Invalid argument: '{}'", arg),
|
_ => panic!("Invalid argument: '{}'", arg),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut interpreter = Interpreter::new();
|
let mut interpreter = Interpreter::new();
|
||||||
|
|
||||||
if let Some(file) = &conf.file {
|
if let Some(file) = &cfg.file {
|
||||||
let code = fs::read_to_string(file).expect(&format!("File not found: '{}'", file));
|
let code = std::fs::read_to_string(file).expect(&format!("File not found: '{}'", file));
|
||||||
interpreter.run_str(&code, conf.print_tokens, conf.print_ast);
|
let tokens = lex(&code);
|
||||||
|
let ast = parse(tokens);
|
||||||
|
|
||||||
|
let prog = compile(&ast);
|
||||||
|
|
||||||
|
// println!("{:?}", prog);
|
||||||
|
|
||||||
|
let mut vm = Vm::new(prog);
|
||||||
|
|
||||||
|
vm.run();
|
||||||
|
|
||||||
|
// interpreter.run_text(&code, cfg.print_tokens, cfg.print_ast);
|
||||||
}
|
}
|
||||||
|
|
||||||
if conf.interactive || conf.file.is_none() {
|
if cfg.interactive || cfg.file.is_none() {
|
||||||
|
|
||||||
let mut code = String::new();
|
let mut code = String::new();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
print!(">> ");
|
print!(">> ");
|
||||||
stdout().flush().unwrap();
|
std::io::stdout().flush().unwrap();
|
||||||
|
|
||||||
code.clear();
|
code.clear();
|
||||||
stdin().read_line(&mut code).unwrap();
|
std::io::stdin().read_line(&mut code).unwrap();
|
||||||
|
let code = code.trim();
|
||||||
if code.trim() == "exit" {
|
|
||||||
|
if code == "exit" {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
interpreter.run_str(&code, conf.print_tokens, conf.print_ast);
|
interpreter.run_text(&code, cfg.print_tokens, cfg.print_ast);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
391
src/parser.rs
391
src/parser.rs
@@ -1,7 +1,9 @@
|
|||||||
use std::iter::Peekable;
|
use std::iter::Peekable;
|
||||||
|
|
||||||
use crate::ast::*;
|
use crate::{
|
||||||
use crate::token::Token;
|
ast::{Ast, BinOpType, Expr, Stmt, UnOpType},
|
||||||
|
token::{Keyword, Literal, Token},
|
||||||
|
};
|
||||||
|
|
||||||
struct Parser<T: Iterator<Item = Token>> {
|
struct Parser<T: Iterator<Item = Token>> {
|
||||||
tokens: Peekable<T>,
|
tokens: Peekable<T>,
|
||||||
@@ -14,202 +16,171 @@ impl<T: Iterator<Item = Token>> Parser<T> {
|
|||||||
Self { tokens }
|
Self { tokens }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the next Token without removing it
|
||||||
|
fn peek(&mut self) -> &Token {
|
||||||
|
self.tokens.peek().unwrap_or(&Token::EoF)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Advance to next Token and return the removed Token
|
||||||
|
fn next(&mut self) -> Token {
|
||||||
|
self.tokens.next().unwrap_or(Token::EoF)
|
||||||
|
}
|
||||||
|
|
||||||
fn parse(&mut self) -> Ast {
|
fn parse(&mut self) -> Ast {
|
||||||
let mut prog = Vec::new();
|
let mut prog = Vec::new();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
match self.peek() {
|
let stmt = match self.peek() {
|
||||||
Token::Semicolon => {
|
Token::Semicolon => {
|
||||||
self.next();
|
self.next();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
Token::EoF => break,
|
Token::EoF => break,
|
||||||
Token::RBraces => {
|
Token::RBrace => break,
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// By default try to lex a statement
|
Token::Keyword(keyword) => match keyword {
|
||||||
_ => prog.push(self.parse_stmt()),
|
Keyword::Let => self.parse_let_stmt(),
|
||||||
}
|
Keyword::While => self.parse_while(),
|
||||||
|
Keyword::If => self.parse_if(),
|
||||||
|
Keyword::For => self.parse_for(),
|
||||||
|
Keyword::Else => panic!("Unexpected else keyword"),
|
||||||
|
},
|
||||||
|
|
||||||
|
Token::Dollar => {
|
||||||
|
self.next();
|
||||||
|
Stmt::Print(self.parse_expr())
|
||||||
|
}
|
||||||
|
Token::DoubleDollar => {
|
||||||
|
self.next();
|
||||||
|
Stmt::DbgPrint(self.parse_expr())
|
||||||
|
}
|
||||||
|
// By default try to parse an expression
|
||||||
|
_ => Stmt::Expr(self.parse_expr()),
|
||||||
|
};
|
||||||
|
|
||||||
|
prog.push(stmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ast { prog }
|
Ast { prog }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_stmt(&mut self) -> Statement {
|
fn parse_for(&mut self) -> Stmt {
|
||||||
match self.peek() {
|
if !matches!(self.next(), Token::Keyword(Keyword::For)) {
|
||||||
Token::Loop => Statement::Loop(self.parse_loop()),
|
panic!("Error parsing for: Expected for token");
|
||||||
|
|
||||||
Token::Print => {
|
|
||||||
self.next();
|
|
||||||
|
|
||||||
let expr = self.parse_expr();
|
|
||||||
|
|
||||||
// After a statement, there must be a semicolon
|
|
||||||
if !matches!(self.next(), Token::Semicolon) {
|
|
||||||
panic!("Expected semicolon after statement");
|
|
||||||
}
|
|
||||||
|
|
||||||
Statement::Print(expr)
|
|
||||||
}
|
|
||||||
|
|
||||||
Token::Return => {
|
|
||||||
self.next();
|
|
||||||
|
|
||||||
let expr = self.parse_expr();
|
|
||||||
|
|
||||||
// After a statement, there must be a semicolon
|
|
||||||
if !matches!(self.next(), Token::Semicolon) {
|
|
||||||
panic!("Expected semicolon after statement");
|
|
||||||
}
|
|
||||||
|
|
||||||
Statement::Return(expr)
|
|
||||||
}
|
|
||||||
|
|
||||||
Token::If => Statement::If(self.parse_if()),
|
|
||||||
|
|
||||||
Token::Fun => {
|
|
||||||
self.next();
|
|
||||||
|
|
||||||
let name = match self.next() {
|
|
||||||
Token::Ident(name) => name,
|
|
||||||
_ => panic!("Error lexing function: Expected ident token"),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut args = Vec::new();
|
|
||||||
|
|
||||||
if !matches!(self.next(), Token::LParen) {
|
|
||||||
panic!("Expected opening parenthesis");
|
|
||||||
}
|
|
||||||
|
|
||||||
while self.peek() != &Token::RParen {
|
|
||||||
let argname = match self.next() {
|
|
||||||
Token::Ident(argname) => argname,
|
|
||||||
_ => panic!("Error lexing function: Expected ident token for argname"),
|
|
||||||
};
|
|
||||||
|
|
||||||
args.push(argname);
|
|
||||||
|
|
||||||
if self.peek() == &Token::Comma {
|
|
||||||
self.next();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
self.next();
|
|
||||||
|
|
||||||
if !matches!(self.next(), Token::LBraces) {
|
|
||||||
panic!("Expected opening braces");
|
|
||||||
}
|
|
||||||
|
|
||||||
let body = self.parse();
|
|
||||||
|
|
||||||
if !matches!(self.next(), Token::RBraces) {
|
|
||||||
panic!("Expected closing braces");
|
|
||||||
}
|
|
||||||
|
|
||||||
Statement::FunDecl(name, args, body)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If it is not a loop, try to lex as an expression
|
|
||||||
_ => {
|
|
||||||
let stmt = Statement::Expr(self.parse_expr());
|
|
||||||
|
|
||||||
// After a statement, there must be a semicolon
|
|
||||||
if !matches!(self.next(), Token::Semicolon) {
|
|
||||||
panic!("Expected semicolon after statement");
|
|
||||||
}
|
|
||||||
|
|
||||||
stmt
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_if(&mut self) -> If {
|
let init = match self.parse_let_stmt() {
|
||||||
if !matches!(self.next(), Token::If) {
|
Stmt::Let(name, rhs) => (name, rhs),
|
||||||
panic!("Error lexing if: Expected if token");
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if !matches!(self.next(), Token::Semicolon) {
|
||||||
|
panic!("Error parsing for: Expected semicolon token");
|
||||||
}
|
}
|
||||||
|
|
||||||
let condition = self.parse_expr();
|
let condition = self.parse_expr();
|
||||||
|
|
||||||
if !matches!(self.next(), Token::LBraces) {
|
if !matches!(self.next(), Token::Semicolon) {
|
||||||
panic!("Error lexing if: Expected '{{'")
|
panic!("Error parsing for: Expected semicolon token");
|
||||||
}
|
}
|
||||||
|
|
||||||
let body_true = self.parse();
|
let advance = self.parse_expr();
|
||||||
|
|
||||||
if !matches!(self.next(), Token::RBraces) {
|
if !matches!(self.next(), Token::LBrace) {
|
||||||
panic!("Error lexing if: Expected '}}'")
|
panic!("Error parsing for: Expected '{{' token");
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut body_false = Ast::default();
|
let body = self.parse();
|
||||||
|
|
||||||
if matches!(self.peek(), Token::Else) {
|
if !matches!(self.next(), Token::RBrace) {
|
||||||
|
panic!("Error parsing for: Expected '}}' token");
|
||||||
|
}
|
||||||
|
|
||||||
|
Stmt::For(init, condition, advance, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_if(&mut self) -> Stmt {
|
||||||
|
if !matches!(self.next(), Token::Keyword(Keyword::If)) {
|
||||||
|
panic!("Error parsing if: Expected if token");
|
||||||
|
}
|
||||||
|
|
||||||
|
let condition = self.parse_expr();
|
||||||
|
|
||||||
|
if !matches!(self.next(), Token::LBrace) {
|
||||||
|
panic!("Error parsing if: Expected '{{' token");
|
||||||
|
}
|
||||||
|
|
||||||
|
let body_if = self.parse();
|
||||||
|
|
||||||
|
if !matches!(self.next(), Token::RBrace) {
|
||||||
|
panic!("Error parsing if: Expected '}}' token");
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut body_else = Ast { prog: Vec::new() };
|
||||||
|
|
||||||
|
if matches!(self.peek(), Token::Keyword(Keyword::Else)) {
|
||||||
self.next();
|
self.next();
|
||||||
|
|
||||||
if !matches!(self.next(), Token::LBraces) {
|
if !matches!(self.next(), Token::LBrace) {
|
||||||
panic!("Error lexing if: Expected '{{'")
|
panic!("Error parsing else: Expected '{{' token");
|
||||||
}
|
}
|
||||||
|
|
||||||
body_false = self.parse();
|
body_else = self.parse();
|
||||||
|
|
||||||
if !matches!(self.next(), Token::RBraces) {
|
if !matches!(self.next(), Token::RBrace) {
|
||||||
panic!("Error lexing if: Expected '}}'")
|
panic!("Error parsing else: Expected '}}' token");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
If {
|
Stmt::If(condition, body_if, body_else)
|
||||||
condition,
|
|
||||||
body_true,
|
|
||||||
body_false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_loop(&mut self) -> Loop {
|
fn parse_while(&mut self) -> Stmt {
|
||||||
if !matches!(self.next(), Token::Loop) {
|
if !matches!(self.next(), Token::Keyword(Keyword::While)) {
|
||||||
panic!("Error lexing loop: Expected loop token");
|
panic!("Error parsing while: Expected while token");
|
||||||
}
|
}
|
||||||
|
|
||||||
let condition = self.parse_expr();
|
let condition = self.parse_expr();
|
||||||
let mut advancement = None;
|
|
||||||
|
|
||||||
let body;
|
if !matches!(self.next(), Token::LBrace) {
|
||||||
|
panic!("Error parsing while: Expected '{{' token");
|
||||||
match self.next() {
|
|
||||||
Token::LBraces => {
|
|
||||||
body = self.parse();
|
|
||||||
}
|
|
||||||
|
|
||||||
Token::Semicolon => {
|
|
||||||
advancement = Some(self.parse_expr());
|
|
||||||
|
|
||||||
if !matches!(self.next(), Token::LBraces) {
|
|
||||||
panic!("Error lexing loop: Expected '{{'")
|
|
||||||
}
|
|
||||||
|
|
||||||
body = self.parse();
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => panic!("Error lexing loop: Expected ';' or '{{'"),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !matches!(self.next(), Token::RBraces) {
|
let body = self.parse();
|
||||||
panic!("Error lexing loop: Expected '}}'")
|
|
||||||
|
if !matches!(self.next(), Token::RBrace) {
|
||||||
|
panic!("Error parsing while: Expected '}}' token");
|
||||||
}
|
}
|
||||||
|
|
||||||
Loop {
|
Stmt::While(condition, body)
|
||||||
condition,
|
|
||||||
advancement,
|
|
||||||
body,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_expr(&mut self) -> Expression {
|
fn parse_let_stmt(&mut self) -> Stmt {
|
||||||
|
if !matches!(self.next(), Token::Keyword(Keyword::Let)) {
|
||||||
|
panic!("Error parsing let: Expected let token");
|
||||||
|
}
|
||||||
|
|
||||||
|
let name = match self.next() {
|
||||||
|
Token::Ident(name) => name,
|
||||||
|
_ => panic!("Error parsing let: Expected identifier after let"),
|
||||||
|
};
|
||||||
|
|
||||||
|
if !matches!(self.next(), Token::Assign) {
|
||||||
|
panic!("Error parsing let: Expected assignment token");
|
||||||
|
}
|
||||||
|
|
||||||
|
let rhs = self.parse_expr();
|
||||||
|
|
||||||
|
Stmt::Let(name, rhs)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_expr(&mut self) -> Expr {
|
||||||
let lhs = self.parse_primary();
|
let lhs = self.parse_primary();
|
||||||
self.parse_expr_precedence(lhs, 0)
|
self.parse_expr_precedence(lhs, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse binary expressions with a precedence equal to or higher than min_prec
|
/// Parse binary expressions with a precedence equal to or higher than min_prec
|
||||||
fn parse_expr_precedence(&mut self, mut lhs: Expression, min_prec: u8) -> Expression {
|
fn parse_expr_precedence(&mut self, mut lhs: Expr, min_prec: u8) -> Expr {
|
||||||
while let Some(binop) = &self.peek().try_to_binop() {
|
while let Some(binop) = &self.peek().try_to_binop() {
|
||||||
// Stop if the next operator has a lower binding power
|
// Stop if the next operator has a lower binding power
|
||||||
if !(binop.precedence() >= min_prec) {
|
if !(binop.precedence() >= min_prec) {
|
||||||
@@ -230,86 +201,40 @@ impl<T: Iterator<Item = Token>> Parser<T> {
|
|||||||
rhs = self.parse_expr_precedence(rhs, binop.precedence() + 1);
|
rhs = self.parse_expr_precedence(rhs, binop.precedence() + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
lhs = Expression::BinOp(binop, lhs.into(), rhs.into());
|
lhs = Expr::BinOp(binop, lhs.into(), rhs.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
lhs
|
lhs
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a primary expression (for now only number)
|
/// Parse a primary expression (for now only number)
|
||||||
fn parse_primary(&mut self) -> Expression {
|
fn parse_primary(&mut self) -> Expr {
|
||||||
match self.next() {
|
match self.next() {
|
||||||
// Literal i64
|
Token::Literal(Literal::I64(val)) => Expr::I64(val),
|
||||||
Token::I64(val) => Expression::I64(val),
|
|
||||||
|
|
||||||
// Literal String
|
Token::Literal(Literal::Str(text)) => Expr::Str(text.into()),
|
||||||
Token::String(text) => Expression::String(text.into()),
|
|
||||||
|
|
||||||
Token::Ident(name) if matches!(self.peek(), Token::LParen) => self.parse_funcall(name),
|
Token::Ident(name) => Expr::Ident(name),
|
||||||
|
|
||||||
Token::Ident(name) => Expression::Var(name),
|
|
||||||
|
|
||||||
// Parentheses grouping
|
|
||||||
Token::LParen => {
|
Token::LParen => {
|
||||||
let inner_expr = self.parse_expr();
|
// The tokens was an opening parenthesis, so parse a full expression again as the
|
||||||
|
// expression inside the parentheses `"(" expr ")"`
|
||||||
|
let inner = self.parse_expr();
|
||||||
|
|
||||||
// Verify that there is a closing parenthesis
|
// If there is no closing parenthesis after the expression, it is a syntax error
|
||||||
if !matches!(self.next(), Token::RParen) {
|
if !matches!(self.next(), Token::RParen) {
|
||||||
panic!("Error parsing primary expr: Exepected closing parenthesis ')'");
|
panic!("Error parsing primary expr: Missing closing parenthesis ')'");
|
||||||
}
|
}
|
||||||
|
|
||||||
inner_expr
|
inner
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unary negation
|
Token::Sub => Expr::UnOp(UnOpType::Neg, self.parse_primary().into()),
|
||||||
Token::Sub => {
|
|
||||||
let operand = self.parse_primary();
|
|
||||||
Expression::UnOp(UnOpType::Negate, operand.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unary bitwise not (bitflip)
|
|
||||||
Token::Tilde => {
|
|
||||||
let operand = self.parse_primary();
|
|
||||||
Expression::UnOp(UnOpType::BNot, operand.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unary logical not
|
|
||||||
Token::LNot => {
|
|
||||||
let operand = self.parse_primary();
|
|
||||||
Expression::UnOp(UnOpType::LNot, operand.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
tok => panic!("Error parsing primary expr: Unexpected Token '{:?}'", tok),
|
tok => panic!("Error parsing primary expr: Unexpected Token '{:?}'", tok),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_funcall(&mut self, name: String) -> Expression {
|
|
||||||
let mut args = Vec::new();
|
|
||||||
|
|
||||||
// Consume (
|
|
||||||
self.next();
|
|
||||||
|
|
||||||
while self.peek() != &Token::RParen {
|
|
||||||
args.push(self.parse_expr());
|
|
||||||
|
|
||||||
if self.peek() == &Token::Comma {
|
|
||||||
self.next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.next();
|
|
||||||
|
|
||||||
Expression::FunCall(name, args)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the next Token without removing it
|
|
||||||
fn peek(&mut self) -> &Token {
|
|
||||||
self.tokens.peek().unwrap_or(&Token::EoF)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Advance to next Token and return the removed Token
|
|
||||||
fn next(&mut self) -> Token {
|
|
||||||
self.tokens.next().unwrap_or(Token::EoF)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse<T: Iterator<Item = Token>, A: IntoIterator<IntoIter = T>>(tokens: A) -> Ast {
|
pub fn parse<T: Iterator<Item = Token>, A: IntoIterator<IntoIter = T>>(tokens: A) -> Ast {
|
||||||
@@ -324,31 +249,27 @@ impl BinOpType {
|
|||||||
/// The operator precedences are derived from the C language operator precedences. While not all
|
/// The operator precedences are derived from the C language operator precedences. While not all
|
||||||
/// C operators are included or the exact same, the precedence oder is the same.
|
/// C operators are included or the exact same, the precedence oder is the same.
|
||||||
/// See: https://en.cppreference.com/w/c/language/operator_precedence
|
/// See: https://en.cppreference.com/w/c/language/operator_precedence
|
||||||
|
|
||||||
fn precedence(&self) -> u8 {
|
fn precedence(&self) -> u8 {
|
||||||
match self {
|
match self {
|
||||||
BinOpType::Declare => 0,
|
BinOpType::Assign => 0,
|
||||||
BinOpType::Assign => 1,
|
BinOpType::BOr => 1,
|
||||||
BinOpType::LOr => 2,
|
BinOpType::BXor => 2,
|
||||||
BinOpType::LAnd => 3,
|
BinOpType::BAnd => 3,
|
||||||
BinOpType::BOr => 4,
|
BinOpType::Equ | BinOpType::Neq => 4,
|
||||||
BinOpType::BXor => 5,
|
BinOpType::Gt | BinOpType::Ge | BinOpType::Lt | BinOpType::Le => 5,
|
||||||
BinOpType::BAnd => 6,
|
BinOpType::Shl | BinOpType::Shr => 6,
|
||||||
BinOpType::EquEqu | BinOpType::NotEqu => 7,
|
BinOpType::Add | BinOpType::Sub => 7,
|
||||||
BinOpType::Less | BinOpType::LessEqu | BinOpType::Greater | BinOpType::GreaterEqu => 8,
|
BinOpType::Mul | BinOpType::Div | BinOpType::Mod => 8,
|
||||||
BinOpType::Shl | BinOpType::Shr => 9,
|
|
||||||
BinOpType::Add | BinOpType::Sub => 10,
|
|
||||||
BinOpType::Mul | BinOpType::Div | BinOpType::Mod => 11,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{parse, BinOpType, Expression};
|
use super::{parse, BinOpType, Expr};
|
||||||
use crate::{
|
use crate::{
|
||||||
parser::{Ast, Statement},
|
parser::{Ast, Stmt},
|
||||||
token::Token,
|
token::{Literal, Token},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -356,34 +277,28 @@ mod tests {
|
|||||||
// Expression: 1 + 2 * 3 + 4
|
// Expression: 1 + 2 * 3 + 4
|
||||||
// With precedence: (1 + (2 * 3)) + 4
|
// With precedence: (1 + (2 * 3)) + 4
|
||||||
let tokens = [
|
let tokens = [
|
||||||
Token::I64(1),
|
Token::Literal(Literal::I64(1)),
|
||||||
Token::Add,
|
Token::Add,
|
||||||
Token::I64(2),
|
Token::Literal(Literal::I64(2)),
|
||||||
Token::Mul,
|
Token::Mul,
|
||||||
Token::I64(3),
|
Token::Literal(Literal::I64(3)),
|
||||||
Token::Sub,
|
Token::Sub,
|
||||||
Token::I64(4),
|
Token::Literal(Literal::I64(4)),
|
||||||
Token::Semicolon,
|
|
||||||
];
|
];
|
||||||
|
|
||||||
let expected = Statement::Expr(Expression::BinOp(
|
let expected = Expr::BinOp(
|
||||||
BinOpType::Sub,
|
BinOpType::Sub,
|
||||||
Expression::BinOp(
|
Expr::BinOp(
|
||||||
BinOpType::Add,
|
BinOpType::Add,
|
||||||
Expression::I64(1).into(),
|
Expr::I64(1).into(),
|
||||||
Expression::BinOp(
|
Expr::BinOp(BinOpType::Mul, Expr::I64(2).into(), Expr::I64(3).into()).into(),
|
||||||
BinOpType::Mul,
|
|
||||||
Expression::I64(2).into(),
|
|
||||||
Expression::I64(3).into(),
|
|
||||||
)
|
|
||||||
.into(),
|
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
Expression::I64(4).into(),
|
Expr::I64(4).into(),
|
||||||
));
|
);
|
||||||
|
|
||||||
let expected = Ast {
|
let expected = Ast {
|
||||||
prog: vec![expected],
|
prog: vec![Stmt::Expr(expected)],
|
||||||
};
|
};
|
||||||
|
|
||||||
let actual = parse(tokens);
|
let actual = parse(tokens);
|
||||||
|
|||||||
127
src/token.rs
127
src/token.rs
@@ -1,45 +1,63 @@
|
|||||||
use crate::ast::BinOpType;
|
use crate::ast::BinOpType;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub enum Token {
|
pub enum Literal {
|
||||||
/// Integer literal (64-bit)
|
/// Integer literal (64-bit)
|
||||||
I64(i64),
|
I64(i64),
|
||||||
|
|
||||||
/// String literal
|
/// String literal ("Some string")
|
||||||
String(String),
|
Str(String),
|
||||||
|
}
|
||||||
|
|
||||||
/// Identifier (name for variables, functions, ...)
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
Ident(String),
|
pub enum Keyword {
|
||||||
|
/// Let identifier (let)
|
||||||
|
Let,
|
||||||
|
|
||||||
/// Loop keyword (loop)
|
/// While (while)
|
||||||
Loop,
|
While,
|
||||||
|
|
||||||
/// Print keyword (print)
|
/// For (for)
|
||||||
Print,
|
For,
|
||||||
|
|
||||||
/// If keyword (if)
|
/// If (if)
|
||||||
If,
|
If,
|
||||||
|
|
||||||
/// Else keyword (else)
|
/// Else (else)
|
||||||
Else,
|
Else,
|
||||||
|
}
|
||||||
|
|
||||||
Fun,
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub enum Token {
|
||||||
|
/// Literal values
|
||||||
|
Literal(Literal),
|
||||||
|
|
||||||
Comma,
|
/// Identifier (variable / function / ... name)
|
||||||
|
Ident(String),
|
||||||
|
|
||||||
Return,
|
/// Specific identifiers that have a special meaning as keywords
|
||||||
|
Keyword(Keyword),
|
||||||
|
|
||||||
/// Left Parenthesis ('(')
|
/// Left parenthesis ('(')
|
||||||
LParen,
|
LParen,
|
||||||
|
|
||||||
/// Right Parenthesis (')')
|
/// Right parentheses (')')
|
||||||
RParen,
|
RParen,
|
||||||
|
|
||||||
/// Left curly braces ({)
|
/// Left brace ({)
|
||||||
LBraces,
|
LBrace,
|
||||||
|
|
||||||
/// Right curly braces (})
|
/// Right brace (})
|
||||||
RBraces,
|
RBrace,
|
||||||
|
|
||||||
|
/// Dollar sign ($)
|
||||||
|
Dollar,
|
||||||
|
|
||||||
|
/// Double Dollar sign ($$)
|
||||||
|
DoubleDollar,
|
||||||
|
|
||||||
|
/// Assignment (single equal) (=)
|
||||||
|
Assign,
|
||||||
|
|
||||||
/// Plus (+)
|
/// Plus (+)
|
||||||
Add,
|
Add,
|
||||||
@@ -56,12 +74,6 @@ pub enum Token {
|
|||||||
/// Percent (%)
|
/// Percent (%)
|
||||||
Mod,
|
Mod,
|
||||||
|
|
||||||
/// Equal Equal (==)
|
|
||||||
EquEqu,
|
|
||||||
|
|
||||||
/// Exclamationmark Equal (!=)
|
|
||||||
NotEqu,
|
|
||||||
|
|
||||||
/// Pipe (|)
|
/// Pipe (|)
|
||||||
BOr,
|
BOr,
|
||||||
|
|
||||||
@@ -71,42 +83,30 @@ pub enum Token {
|
|||||||
/// Circumflex (^)
|
/// Circumflex (^)
|
||||||
BXor,
|
BXor,
|
||||||
|
|
||||||
/// Logical AND (&&)
|
|
||||||
LAnd,
|
|
||||||
|
|
||||||
/// Logical OR (||)
|
|
||||||
LOr,
|
|
||||||
|
|
||||||
/// Shift Left (<<)
|
/// Shift Left (<<)
|
||||||
Shl,
|
Shl,
|
||||||
|
|
||||||
/// Shift Right (>>)
|
/// Shift Right (>>)
|
||||||
Shr,
|
Shr,
|
||||||
|
|
||||||
/// Tilde (~)
|
/// Equal sign (==)
|
||||||
Tilde,
|
|
||||||
|
|
||||||
/// Logical not (!)
|
|
||||||
LNot,
|
|
||||||
|
|
||||||
/// Left angle bracket (<)
|
|
||||||
LAngle,
|
|
||||||
|
|
||||||
/// Right angle bracket (>)
|
|
||||||
RAngle,
|
|
||||||
|
|
||||||
/// Left angle bracket Equal (<=)
|
|
||||||
LAngleEqu,
|
|
||||||
|
|
||||||
/// Left angle bracket Equal (>=)
|
|
||||||
RAngleEqu,
|
|
||||||
|
|
||||||
/// Left arrow (<-)
|
|
||||||
LArrow,
|
|
||||||
|
|
||||||
/// Equal Sign (=)
|
|
||||||
Equ,
|
Equ,
|
||||||
|
|
||||||
|
/// Not Equal sign (!=)
|
||||||
|
Neq,
|
||||||
|
|
||||||
|
/// Greater than (>)
|
||||||
|
Gt,
|
||||||
|
|
||||||
|
/// Greater or equal (>=)
|
||||||
|
Ge,
|
||||||
|
|
||||||
|
/// Less than (<)
|
||||||
|
Lt,
|
||||||
|
|
||||||
|
/// Less or equal (<=)
|
||||||
|
Le,
|
||||||
|
|
||||||
/// Semicolon (;)
|
/// Semicolon (;)
|
||||||
Semicolon,
|
Semicolon,
|
||||||
|
|
||||||
@@ -128,23 +128,18 @@ impl Token {
|
|||||||
Token::BOr => BinOpType::BOr,
|
Token::BOr => BinOpType::BOr,
|
||||||
Token::BXor => BinOpType::BXor,
|
Token::BXor => BinOpType::BXor,
|
||||||
|
|
||||||
Token::LAnd => BinOpType::LAnd,
|
|
||||||
Token::LOr => BinOpType::LOr,
|
|
||||||
|
|
||||||
Token::Shl => BinOpType::Shl,
|
Token::Shl => BinOpType::Shl,
|
||||||
Token::Shr => BinOpType::Shr,
|
Token::Shr => BinOpType::Shr,
|
||||||
|
|
||||||
Token::EquEqu => BinOpType::EquEqu,
|
Token::Equ => BinOpType::Equ,
|
||||||
Token::NotEqu => BinOpType::NotEqu,
|
Token::Neq => BinOpType::Neq,
|
||||||
|
|
||||||
Token::LAngle => BinOpType::Less,
|
Token::Gt => BinOpType::Gt,
|
||||||
Token::LAngleEqu => BinOpType::LessEqu,
|
Token::Ge => BinOpType::Ge,
|
||||||
|
Token::Lt => BinOpType::Lt,
|
||||||
|
Token::Le => BinOpType::Le,
|
||||||
|
|
||||||
Token::RAngle => BinOpType::Greater,
|
Token::Assign => BinOpType::Assign,
|
||||||
Token::RAngleEqu => BinOpType::GreaterEqu,
|
|
||||||
|
|
||||||
Token::LArrow => BinOpType::Declare,
|
|
||||||
Token::Equ => BinOpType::Assign,
|
|
||||||
|
|
||||||
_ => return None,
|
_ => return None,
|
||||||
})
|
})
|
||||||
|
|||||||
187
src/vm.rs
Normal file
187
src/vm.rs
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
use crate::{bytecode::OP, interpreter::Value};
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct Vm {
|
||||||
|
prog: Vec<u32>,
|
||||||
|
ip: usize,
|
||||||
|
stack: Vec<Value>,
|
||||||
|
|
||||||
|
/// This isn't actually a heap. It's actually still more of a f*cked up stack
|
||||||
|
heap: Vec<Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Vm {
|
||||||
|
pub fn new(prog: Vec<u32>) -> Self {
|
||||||
|
Self {
|
||||||
|
prog,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(&mut self) {
|
||||||
|
while let Some(op) = self.prog.get(self.ip).copied().map(|op| unsafe { std::mem::transmute::<u32, OP>(op) }) {
|
||||||
|
self.ip += 1;
|
||||||
|
|
||||||
|
match op {
|
||||||
|
OP::Push => {
|
||||||
|
let val = self.read_i64();
|
||||||
|
self.stack.push(Value::I64(val));
|
||||||
|
}
|
||||||
|
OP::Pop => {
|
||||||
|
self.stack.pop();
|
||||||
|
}
|
||||||
|
OP::Load => {
|
||||||
|
let addr = self.read_i64() as usize;
|
||||||
|
|
||||||
|
if let Some(val) = self.heap.get(addr) {
|
||||||
|
self.stack.push(val.clone());
|
||||||
|
} else {
|
||||||
|
panic!("Trying to load from uninitialized heap");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OP::Store => {
|
||||||
|
let val = self
|
||||||
|
.stack
|
||||||
|
.pop()
|
||||||
|
.expect("Trying to pop value from stack for storing");
|
||||||
|
let addr = self.read_i64() as usize;
|
||||||
|
if self.heap.len() == addr {
|
||||||
|
self.heap.push(val);
|
||||||
|
} else {
|
||||||
|
self.heap[addr] = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OP::Print => {
|
||||||
|
let val = self
|
||||||
|
.stack
|
||||||
|
.pop()
|
||||||
|
.expect("Trying to pop value from stack for printing");
|
||||||
|
print!("{}", val);
|
||||||
|
}
|
||||||
|
OP::DbgPrint => {
|
||||||
|
let val = self
|
||||||
|
.stack
|
||||||
|
.pop()
|
||||||
|
.expect("Trying to pop value from stack for printing");
|
||||||
|
print!("{:?}", val);
|
||||||
|
}
|
||||||
|
|
||||||
|
OP::Add => {
|
||||||
|
let vals = self.pop2_i64();
|
||||||
|
self.stack.push(Value::I64(vals.0 + vals.1))
|
||||||
|
}
|
||||||
|
OP::Subtract => {
|
||||||
|
let vals = self.pop2_i64();
|
||||||
|
self.stack.push(Value::I64(vals.0 - vals.1))
|
||||||
|
}
|
||||||
|
OP::Multiply => {
|
||||||
|
let vals = self.pop2_i64();
|
||||||
|
self.stack.push(Value::I64(vals.0 * vals.1))
|
||||||
|
}
|
||||||
|
OP::Divide => {
|
||||||
|
let vals = self.pop2_i64();
|
||||||
|
self.stack.push(Value::I64(vals.0 / vals.1))
|
||||||
|
}
|
||||||
|
OP::Modulo => {
|
||||||
|
let vals = self.pop2_i64();
|
||||||
|
self.stack.push(Value::I64(vals.0 % vals.1))
|
||||||
|
}
|
||||||
|
OP::Eq => {
|
||||||
|
let vals = self.pop2_i64();
|
||||||
|
self.stack
|
||||||
|
.push(Value::I64(if vals.0 == vals.1 { 1 } else { 0 }))
|
||||||
|
}
|
||||||
|
OP::Neq => {
|
||||||
|
let vals = self.pop2_i64();
|
||||||
|
self.stack
|
||||||
|
.push(Value::I64(if vals.0 != vals.1 { 1 } else { 0 }))
|
||||||
|
}
|
||||||
|
OP::Gt => {
|
||||||
|
let vals = self.pop2_i64();
|
||||||
|
self.stack
|
||||||
|
.push(Value::I64(if vals.0 > vals.1 { 1 } else { 0 }))
|
||||||
|
}
|
||||||
|
OP::Ge => {
|
||||||
|
let vals = self.pop2_i64();
|
||||||
|
self.stack
|
||||||
|
.push(Value::I64(if vals.0 >= vals.1 { 1 } else { 0 }))
|
||||||
|
}
|
||||||
|
OP::Lt => {
|
||||||
|
let vals = self.pop2_i64();
|
||||||
|
self.stack
|
||||||
|
.push(Value::I64(if vals.0 < vals.1 { 1 } else { 0 }))
|
||||||
|
}
|
||||||
|
OP::Le => {
|
||||||
|
let vals = self.pop2_i64();
|
||||||
|
self.stack
|
||||||
|
.push(Value::I64(if vals.0 <= vals.1 { 1 } else { 0 }))
|
||||||
|
}
|
||||||
|
OP::BOr => {
|
||||||
|
let vals = self.pop2_i64();
|
||||||
|
self.stack.push(Value::I64(vals.0 | vals.1))
|
||||||
|
}
|
||||||
|
OP::BAnd => {
|
||||||
|
let vals = self.pop2_i64();
|
||||||
|
self.stack.push(Value::I64(vals.0 & vals.1))
|
||||||
|
}
|
||||||
|
OP::BXor => {
|
||||||
|
let vals = self.pop2_i64();
|
||||||
|
self.stack.push(Value::I64(vals.0 ^ vals.1))
|
||||||
|
}
|
||||||
|
OP::Shl => {
|
||||||
|
let vals = self.pop2_i64();
|
||||||
|
self.stack.push(Value::I64(vals.0 << vals.1))
|
||||||
|
}
|
||||||
|
OP::Shr => {
|
||||||
|
let vals = self.pop2_i64();
|
||||||
|
self.stack.push(Value::I64(vals.0 >> vals.1))
|
||||||
|
}
|
||||||
|
OP::Jump => {
|
||||||
|
self.ip = self.read_i64() as usize;
|
||||||
|
}
|
||||||
|
OP::JumpTrue => {
|
||||||
|
let jmp_target = self.read_i64() as usize;
|
||||||
|
if !matches!(self.stack.pop(), Some(Value::I64(0))) {
|
||||||
|
self.ip = jmp_target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OP::JumpFalse => {
|
||||||
|
let jmp_target = self.read_i64() as usize;
|
||||||
|
if matches!(self.stack.pop(), Some(Value::I64(0))) {
|
||||||
|
self.ip = jmp_target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pop2_i64(&mut self) -> (i64, i64) {
|
||||||
|
let rhs = self.stack.pop();
|
||||||
|
let lhs = self.stack.pop();
|
||||||
|
match (lhs, rhs) {
|
||||||
|
(Some(Value::I64(lhs)), Some(Value::I64(rhs))) => (lhs, rhs),
|
||||||
|
_ => panic!("Invalid data for add"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_i64(&mut self) -> i64 {
|
||||||
|
let mut val = *self.prog.get(self.ip).unwrap() as i64;
|
||||||
|
val |= (*self.prog.get(self.ip + 1).unwrap() as i64) << 32;
|
||||||
|
|
||||||
|
// let mut bytes = [0; 8];
|
||||||
|
// bytes.copy_from_slice(&self.prog[self.ip..self.ip+8]);
|
||||||
|
// val = i64::from_le_bytes(bytes);
|
||||||
|
|
||||||
|
// for i in 0 .. 8 {
|
||||||
|
// if let Some(tmp) = self.prog.get(self.ip + i).copied() {
|
||||||
|
// val |= ((tmp as i64) << i*8) as i64;
|
||||||
|
// } else {
|
||||||
|
// panic!("Expected Value as next OP")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
self.ip += 2;
|
||||||
|
|
||||||
|
val
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user