implemented list literals including operations on lists
experimenting with arenas
This commit is contained in:
parent
6f985cbab1
commit
a8850cc547
10 changed files with 138 additions and 633 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
|
@ -124,6 +124,9 @@ name = "bumpalo"
|
||||||
version = "3.19.0"
|
version = "3.19.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
|
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
|
|
@ -197,6 +200,7 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"axum",
|
"axum",
|
||||||
|
"bumpalo",
|
||||||
"chrono",
|
"chrono",
|
||||||
"dotenv",
|
"dotenv",
|
||||||
"log",
|
"log",
|
||||||
|
|
|
||||||
|
|
@ -20,3 +20,4 @@ tracing-subscriber = "0.3.20"
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
tower-livereload = "0.9.6"
|
tower-livereload = "0.9.6"
|
||||||
log = "0.4.28"
|
log = "0.4.28"
|
||||||
|
bumpalo = { version = "3.19", features = ["collections", "boxed", "serde"] }
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::tokens::TokenType::{Bang, Bool, Char, Colon, Date, Eol, Equal, F32, F64, False, FloatingPoint, Fn, Greater, GreaterEqual, GreaterGreater, I32, I64, Identifier, Indent, Integer, LeftParen, Less, LessEqual, LessLess, Let, ListType, MapType, Minus, Object, Plus, Print, RightParen, SingleRightArrow, Slash, Star, True, U32, U64, StringType};
|
use crate::tokens::TokenType::{Bang, Bool, Char, Colon, Date, Eol, Equal, F32, F64, False, FloatingPoint, Fn, Greater, GreaterEqual, GreaterGreater, I32, I64, Identifier, If, Indent, Integer, LeftBracket, LeftParen, Less, LessEqual, LessLess, Let, ListType, MapType, Minus, Object, Plus, Print, RightParen, SingleRightArrow, Slash, Star, StringType, True, U32, U64, RightBracket};
|
||||||
use crate::tokens::{Token, TokenType};
|
use crate::tokens::{Token, TokenType};
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
|
|
@ -282,7 +282,9 @@ impl AstCompiler {
|
||||||
|
|
||||||
fn primary(&mut self) -> anyhow::Result<Expression> {
|
fn primary(&mut self) -> anyhow::Result<Expression> {
|
||||||
debug!("primary {:?}", self.peek());
|
debug!("primary {:?}", self.peek());
|
||||||
Ok(if self.match_token(vec![False]) {
|
Ok(if self.match_token(vec![LeftBracket]) {
|
||||||
|
self.list()?
|
||||||
|
} else if self.match_token(vec![False]) {
|
||||||
Expression::Literal {
|
Expression::Literal {
|
||||||
line: self.peek().line,
|
line: self.peek().line,
|
||||||
literaltype: Bool,
|
literaltype: Bool,
|
||||||
|
|
@ -330,6 +332,22 @@ impl AstCompiler {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn list(&mut self) -> anyhow::Result<Expression> {
|
||||||
|
let mut list = vec![];
|
||||||
|
while !self.match_token(vec![RightBracket]){
|
||||||
|
list.push(self.expression()?);
|
||||||
|
if self.peek().token_type == TokenType::Comma {
|
||||||
|
self.advance();
|
||||||
|
} else {
|
||||||
|
self.consume(RightBracket, "Expect ']' after list.")?;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Expression::List {
|
||||||
|
values: list, literaltype: ListType, line: self.peek().line},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn variable_lookup(&mut self, token: &Token) -> anyhow::Result<Expression> {
|
fn variable_lookup(&mut self, token: &Token) -> anyhow::Result<Expression> {
|
||||||
let (var_name, var_type) = self
|
let (var_name, var_type) = self
|
||||||
.vars
|
.vars
|
||||||
|
|
@ -437,9 +455,11 @@ fn calculate_type(
|
||||||
declared_type: Option<TokenType>,
|
declared_type: Option<TokenType>,
|
||||||
inferred_type: TokenType,
|
inferred_type: TokenType,
|
||||||
) -> anyhow::Result<TokenType> {
|
) -> anyhow::Result<TokenType> {
|
||||||
println!("declared type {:?} inferred type: {:?}", declared_type, inferred_type);
|
println!(
|
||||||
|
"declared type {:?} inferred type: {:?}",
|
||||||
|
declared_type, inferred_type
|
||||||
|
);
|
||||||
Ok(if let Some(declared_type) = declared_type {
|
Ok(if let Some(declared_type) = declared_type {
|
||||||
|
|
||||||
if declared_type != inferred_type {
|
if declared_type != inferred_type {
|
||||||
match (declared_type, inferred_type) {
|
match (declared_type, inferred_type) {
|
||||||
(I32, I64) => I32,
|
(I32, I64) => I32,
|
||||||
|
|
@ -448,7 +468,7 @@ fn calculate_type(
|
||||||
(F64, I64) => F64,
|
(F64, I64) => F64,
|
||||||
(U64, I64) => U64,
|
(U64, I64) => U64,
|
||||||
(U64, I32) => U64,
|
(U64, I32) => U64,
|
||||||
(StringType, Text) => StringType,
|
(StringType, _) => StringType, // meh, this all needs rigorous testing
|
||||||
_ => {
|
_ => {
|
||||||
return Err(anyhow::anyhow!(
|
return Err(anyhow::anyhow!(
|
||||||
"Incompatible types. Expected {}, found {}",
|
"Incompatible types. Expected {}, found {}",
|
||||||
|
|
@ -464,7 +484,6 @@ fn calculate_type(
|
||||||
match inferred_type {
|
match inferred_type {
|
||||||
Integer | I64 => I64,
|
Integer | I64 => I64,
|
||||||
FloatingPoint => F64,
|
FloatingPoint => F64,
|
||||||
Text => Text,
|
|
||||||
Bool => Bool,
|
Bool => Bool,
|
||||||
Date => Date,
|
Date => Date,
|
||||||
ListType => ListType,
|
ListType => ListType,
|
||||||
|
|
@ -532,6 +551,11 @@ pub enum Expression {
|
||||||
literaltype: TokenType,
|
literaltype: TokenType,
|
||||||
value: Value,
|
value: Value,
|
||||||
},
|
},
|
||||||
|
List {
|
||||||
|
line: usize,
|
||||||
|
literaltype: TokenType,
|
||||||
|
values: Vec<Expression>,
|
||||||
|
},
|
||||||
Variable {
|
Variable {
|
||||||
line: usize,
|
line: usize,
|
||||||
name: String,
|
name: String,
|
||||||
|
|
@ -550,8 +574,9 @@ impl Expression {
|
||||||
match self {
|
match self {
|
||||||
Self::Binary { line, .. } => *line,
|
Self::Binary { line, .. } => *line,
|
||||||
Self::Unary { line, .. } => *line,
|
Self::Unary { line, .. } => *line,
|
||||||
Self::Grouping { line, expression } => *line,
|
Self::Grouping { line, .. } => *line,
|
||||||
Self::Literal { line, .. } => *line,
|
Self::Literal { line, .. } => *line,
|
||||||
|
Self::List { line, .. } => *line,
|
||||||
Self::Variable { line, .. } => *line,
|
Self::Variable { line, .. } => *line,
|
||||||
Self::FunctionCall { line, .. } => *line,
|
Self::FunctionCall { line, .. } => *line,
|
||||||
}
|
}
|
||||||
|
|
@ -615,8 +640,9 @@ impl Expression {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Self::Grouping { line, expression } => expression.infer_type(),
|
Self::Grouping { expression,.. } => expression.infer_type(),
|
||||||
Self::Literal { literaltype, .. } => literaltype.clone(),
|
Self::Literal { literaltype, .. } => literaltype.clone(),
|
||||||
|
Self::List { literaltype, .. } => literaltype.clone(),
|
||||||
Self::Unary { right, .. } => right.infer_type(),
|
Self::Unary { right, .. } => right.infer_type(),
|
||||||
Self::Variable { var_type, .. } => var_type.clone(),
|
Self::Variable { var_type, .. } => var_type.clone(),
|
||||||
Self::FunctionCall { return_type, .. } => return_type.clone(),
|
Self::FunctionCall { return_type, .. } => return_type.clone(),
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
use crate::ast_compiler::{Expression, Function, Parameter, Statement};
|
use crate::ast_compiler::{Expression, Function, Statement};
|
||||||
use crate::chunk::Chunk;
|
use crate::chunk::Chunk;
|
||||||
use crate::tokens::TokenType;
|
use crate::tokens::TokenType;
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
use crate::vm::{
|
use crate::vm::{
|
||||||
OP_ADD, OP_AND, OP_BITAND, OP_BITOR, OP_BITXOR, OP_CALL, OP_CONSTANT, OP_DEF_BOOL, OP_DEF_CHAR,
|
OP_ADD, OP_AND, OP_BITAND, OP_BITOR, OP_BITXOR, OP_CALL, OP_CONSTANT, OP_DEF_BOOL, OP_DEF_CHAR,
|
||||||
OP_DEF_DATE, OP_DEF_F32, OP_DEF_F64, OP_DEF_FN, OP_DEF_I32, OP_DEF_I64, OP_DEF_LIST,
|
OP_DEF_DATE, OP_DEF_F32, OP_DEF_F64, OP_DEF_I32, OP_DEF_I64, OP_DEF_LIST,
|
||||||
OP_DEF_MAP, OP_DEF_STRING, OP_DEF_STRUCT, OP_DEFINE, OP_DIVIDE, OP_EQUAL, OP_GET, OP_GREATER,
|
OP_DEF_MAP, OP_DEF_STRING, OP_DEF_STRUCT, OP_DEFINE, OP_DIVIDE, OP_EQUAL, OP_GET, OP_GREATER,
|
||||||
OP_GREATER_EQUAL, OP_LESS, OP_LESS_EQUAL, OP_MULTIPLY, OP_NEGATE, OP_NOT, OP_OR, OP_PRINT,
|
OP_GREATER_EQUAL, OP_LESS, OP_LESS_EQUAL, OP_MULTIPLY, OP_NEGATE, OP_NOT, OP_OR,
|
||||||
OP_RETURN, OP_SHL, OP_SHR, OP_SUBTRACT,
|
OP_PRINT, OP_RETURN, OP_SHL, OP_SHR, OP_SUBTRACT,
|
||||||
};
|
};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
|
@ -71,7 +71,10 @@ impl Compiler {
|
||||||
let name_index = self.chunk.add_constant(Value::String(name.lexeme.clone()));
|
let name_index = self.chunk.add_constant(Value::String(name.lexeme.clone()));
|
||||||
self.vars.insert(name.lexeme.clone(), name_index);
|
self.vars.insert(name.lexeme.clone(), name_index);
|
||||||
self.compile_expression(initializer)?;
|
self.compile_expression(initializer)?;
|
||||||
self.define_variable(var_type, name_index)?
|
self.define_variable(var_type, name_index)?;
|
||||||
|
if let Expression::List {values, .. } = initializer {
|
||||||
|
self.emit_byte(values.len() as u16);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Statement::PrintStmt { value } => {
|
Statement::PrintStmt { value } => {
|
||||||
self.compile_expression(value)?;
|
self.compile_expression(value)?;
|
||||||
|
|
@ -84,8 +87,7 @@ impl Compiler {
|
||||||
let function_name = function.name.lexeme.clone();
|
let function_name = function.name.lexeme.clone();
|
||||||
let compiled_function = compile_function(function)?;
|
let compiled_function = compile_function(function)?;
|
||||||
let name_index = self.chunk.add_function(compiled_function);
|
let name_index = self.chunk.add_function(compiled_function);
|
||||||
self.functions
|
self.functions.insert(function_name, name_index);
|
||||||
.insert(function_name, name_index);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -108,6 +110,12 @@ impl Compiler {
|
||||||
self.emit_bytes(OP_GET, *name_index as u16);
|
self.emit_bytes(OP_GET, *name_index as u16);
|
||||||
}
|
}
|
||||||
Expression::Literal { value, .. } => self.emit_constant(value),
|
Expression::Literal { value, .. } => self.emit_constant(value),
|
||||||
|
Expression::List { values, .. } => {
|
||||||
|
for expr in values {
|
||||||
|
self.compile_expression(expr)?;
|
||||||
|
}
|
||||||
|
// self.emit_bytes(OP_NEW_LIST, values.len() as u16);
|
||||||
|
}
|
||||||
Expression::Grouping { expression, .. } => self.compile_expression(expression)?,
|
Expression::Grouping { expression, .. } => self.compile_expression(expression)?,
|
||||||
Expression::Unary {
|
Expression::Unary {
|
||||||
operator, right, ..
|
operator, right, ..
|
||||||
|
|
|
||||||
21
src/chunk.rs
21
src/chunk.rs
|
|
@ -1,10 +1,9 @@
|
||||||
use std::collections::HashMap;
|
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
use crate::vm::{
|
use crate::vm::{
|
||||||
OP_ADD, OP_BITAND, OP_BITOR, OP_BITXOR, OP_CONSTANT, OP_DIVIDE, OP_MULTIPLY,
|
OP_ADD, OP_BITAND, OP_BITOR, OP_BITXOR, OP_CALL, OP_CONSTANT, OP_DEF_BOOL, OP_DEF_F32,
|
||||||
OP_NEGATE, OP_RETURN, OP_SUBTRACT, OP_NOT, OP_SHL, OP_SHR, OP_LESS, OP_LESS_EQUAL,
|
OP_DEF_F64, OP_DEF_I32, OP_DEF_I64, OP_DEF_LIST, OP_DEF_STRING, OP_DEFINE, OP_DIVIDE, OP_EQUAL,
|
||||||
OP_GREATER, OP_GREATER_EQUAL, OP_EQUAL, OP_PRINT, OP_POP, OP_DEFINE, OP_GET,OP_DEF_STRING,
|
OP_GET, OP_GREATER, OP_GREATER_EQUAL, OP_LESS, OP_LESS_EQUAL, OP_MULTIPLY, OP_NEGATE,
|
||||||
OP_DEF_I32, OP_DEF_I64, OP_DEF_F32, OP_DEF_F64, OP_DEF_BOOL, OP_CALL
|
OP_NOT, OP_POP, OP_PRINT, OP_RETURN, OP_SHL, OP_SHR, OP_SUBTRACT,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct Chunk {
|
pub struct Chunk {
|
||||||
|
|
@ -12,10 +11,9 @@ pub struct Chunk {
|
||||||
pub code: Vec<u16>,
|
pub code: Vec<u16>,
|
||||||
pub constants: Vec<Value>,
|
pub constants: Vec<Value>,
|
||||||
lines: Vec<usize>,
|
lines: Vec<usize>,
|
||||||
pub(crate) functions: Vec<Chunk>
|
pub(crate) functions: Vec<Chunk>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl Chunk {
|
impl Chunk {
|
||||||
pub fn new(name: &str) -> Chunk {
|
pub fn new(name: &str) -> Chunk {
|
||||||
Chunk {
|
Chunk {
|
||||||
|
|
@ -92,6 +90,7 @@ impl Chunk {
|
||||||
OP_DEF_BOOL => self.constant_inst("DEFBOOL", offset),
|
OP_DEF_BOOL => self.constant_inst("DEFBOOL", offset),
|
||||||
OP_CALL => self.call_inst("CALL", offset),
|
OP_CALL => self.call_inst("CALL", offset),
|
||||||
OP_GET => self.constant_inst("GET", offset),
|
OP_GET => self.constant_inst("GET", offset),
|
||||||
|
OP_DEF_LIST => self.new_inst("DEFLIST", offset),
|
||||||
_ => {
|
_ => {
|
||||||
println!("Unknown instruction {}", instruction);
|
println!("Unknown instruction {}", instruction);
|
||||||
offset + 1
|
offset + 1
|
||||||
|
|
@ -111,6 +110,14 @@ impl Chunk {
|
||||||
offset + 3
|
offset + 3
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn new_inst(&self, name: &str, offset: usize) -> usize {
|
||||||
|
let constant = self.code[offset + 1];
|
||||||
|
let len = self.code[offset + 2];
|
||||||
|
print!("{} len: {}:", name, len);
|
||||||
|
self.print_value(&self.constants[constant as usize]);
|
||||||
|
offset + 3
|
||||||
|
}
|
||||||
|
|
||||||
fn constant_inst(&self, name: &str, offset: usize) -> usize {
|
fn constant_inst(&self, name: &str, offset: usize) -> usize {
|
||||||
let constant = self.code[offset + 1];
|
let constant = self.code[offset + 1];
|
||||||
print!("{} {}:", name, constant);
|
print!("{} {}:", name, constant);
|
||||||
|
|
|
||||||
554
src/compiler.rs
554
src/compiler.rs
|
|
@ -1,554 +0,0 @@
|
||||||
use crate::chunk::Chunk;
|
|
||||||
use crate::scanner::scan;
|
|
||||||
use crate::tokens::{Token, TokenType};
|
|
||||||
use crate::value::Value;
|
|
||||||
use crate::vm::{
|
|
||||||
OP_ADD, OP_BITAND, OP_BITOR, OP_BITXOR, OP_CONSTANT, OP_DEF_BOOL, OP_DEF_CHAR, OP_DEF_DATE,
|
|
||||||
OP_DEF_F64, OP_DEF_I32, OP_DEF_I64, OP_DEF_LIST, OP_DEF_MAP, OP_DEF_STRING, OP_DEF_STRUCT,
|
|
||||||
OP_DEFINE, OP_DIVIDE, OP_EQUAL, OP_GET, OP_GREATER, OP_GREATER_EQUAL, OP_LESS, OP_LESS_EQUAL,
|
|
||||||
OP_MULTIPLY, OP_NEGATE, OP_NOT, OP_POP, OP_PRINT, OP_RETURN, OP_SHL, OP_SHR, OP_SUBTRACT,
|
|
||||||
};
|
|
||||||
use anyhow::anyhow;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::sync::LazyLock;
|
|
||||||
use tracing::debug;
|
|
||||||
|
|
||||||
macro_rules! parse_num {
|
|
||||||
($s:ident, $variant:ident, $number:ident) => {{
|
|
||||||
$s.typestack.push(TokenType::$variant);
|
|
||||||
Value::$variant($number.parse()?)
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn compile(source: &str) -> anyhow::Result<Chunk> {
|
|
||||||
let tokens = scan(source);
|
|
||||||
debug!("Scanned tokens: {:?}", tokens);
|
|
||||||
|
|
||||||
let mut compiler = Compiler {
|
|
||||||
source: source.lines().map(|s| s.to_string()).collect(),
|
|
||||||
chunk: Chunk::new("main"),
|
|
||||||
previous_token: &tokens[0],
|
|
||||||
current_token: &tokens[0],
|
|
||||||
tokens: &tokens,
|
|
||||||
current: 0,
|
|
||||||
typestack: vec![],
|
|
||||||
locals: vec![],
|
|
||||||
previous: 0,
|
|
||||||
had_error: false,
|
|
||||||
};
|
|
||||||
compiler.compile()
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Compiler<'a> {
|
|
||||||
source: Vec<String>,
|
|
||||||
chunk: Chunk,
|
|
||||||
tokens: &'a Vec<Token>,
|
|
||||||
current: usize,
|
|
||||||
previous_token: &'a Token,
|
|
||||||
current_token: &'a Token,
|
|
||||||
typestack: Vec<TokenType>,
|
|
||||||
locals: Vec<String>,
|
|
||||||
previous: usize,
|
|
||||||
had_error: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> Compiler<'a> {
|
|
||||||
fn compile(mut self) -> anyhow::Result<Chunk> {
|
|
||||||
while !self.match_token(TokenType::Eof) {
|
|
||||||
self.declaration()?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// self.expression()?;
|
|
||||||
|
|
||||||
// self.consume(TokenType::Eof, "Expect end of expression.")?;
|
|
||||||
self.emit_byte(OP_RETURN);
|
|
||||||
Ok(self.chunk)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn declaration(&mut self) -> anyhow::Result<()> {
|
|
||||||
if self.match_token(TokenType::Let) {
|
|
||||||
self.let_declaration()
|
|
||||||
} else {
|
|
||||||
self.statement()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn let_declaration(&mut self) -> anyhow::Result<()> {
|
|
||||||
let index = self.parse_variable("Expect variable name")?;
|
|
||||||
let mut declared_type = None;
|
|
||||||
if self.check(TokenType::Colon) {
|
|
||||||
self.consume(TokenType::Colon, "must not happen")?;
|
|
||||||
match self.current_token.token_type {
|
|
||||||
TokenType::I32
|
|
||||||
| TokenType::I64
|
|
||||||
| TokenType::U32
|
|
||||||
| TokenType::U64
|
|
||||||
| TokenType::Date
|
|
||||||
| TokenType::StringType
|
|
||||||
| TokenType::Char
|
|
||||||
| TokenType::Bool
|
|
||||||
| TokenType::ListType
|
|
||||||
| TokenType::MapType => declared_type = Some(self.current_token.token_type),
|
|
||||||
_ => return Err(anyhow!("Invalid type {:?}", self.current_token.token_type)),
|
|
||||||
}
|
|
||||||
self.advance()?;
|
|
||||||
}
|
|
||||||
if self.match_token(TokenType::Equal) {
|
|
||||||
self.expression(declared_type)?;
|
|
||||||
let derived_type = Some(&self.previous_token.token_type);
|
|
||||||
self.consume(TokenType::Eol, "Expect end of line")?;
|
|
||||||
self.define_variable(declared_type, derived_type, index)?;
|
|
||||||
} else {
|
|
||||||
return Err(anyhow!(
|
|
||||||
"You cannot declare a variable without initializing it."
|
|
||||||
));
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_variable(&mut self, message: &str) -> anyhow::Result<usize> {
|
|
||||||
self.consume(TokenType::Identifier, message)?;
|
|
||||||
self.identifier_constant(self.previous_token)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn identifier_constant(&mut self, token: &Token) -> anyhow::Result<usize> {
|
|
||||||
let name = token.lexeme.clone();
|
|
||||||
let index = self.chunk.add_constant(Value::String(name));
|
|
||||||
Ok(index)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn define_variable(
|
|
||||||
&mut self,
|
|
||||||
var_type: Option<TokenType>,
|
|
||||||
derived_type: Option<&TokenType>,
|
|
||||||
index: usize,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
let def_op = match var_type {
|
|
||||||
Some(TokenType::I32) => OP_DEF_I32,
|
|
||||||
Some(TokenType::I64) => OP_DEF_I64,
|
|
||||||
Some(TokenType::U32) => OP_DEF_I64,
|
|
||||||
Some(TokenType::U64) => OP_DEF_I64,
|
|
||||||
Some(TokenType::Date) => OP_DEF_DATE,
|
|
||||||
Some(TokenType::StringType) => OP_DEF_STRING,
|
|
||||||
Some(TokenType::Char) => OP_DEF_CHAR,
|
|
||||||
Some(TokenType::Bool) => OP_DEF_BOOL,
|
|
||||||
Some(TokenType::ListType) => OP_DEF_LIST,
|
|
||||||
Some(TokenType::MapType) => OP_DEF_MAP,
|
|
||||||
Some(TokenType::Object) => OP_DEF_STRUCT,
|
|
||||||
_ => match derived_type {
|
|
||||||
Some(TokenType::StringType) => OP_DEF_STRING,
|
|
||||||
Some(TokenType::Bool) => OP_DEF_BOOL,
|
|
||||||
Some(TokenType::Char) => OP_DEF_CHAR,
|
|
||||||
Some(TokenType::F64) => OP_DEF_F64,
|
|
||||||
Some(TokenType::I64) => OP_DEF_I64,
|
|
||||||
Some(TokenType::ListType) => OP_DEF_LIST,
|
|
||||||
Some(TokenType::MapType) => OP_DEF_MAP,
|
|
||||||
_ => OP_DEFINE,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
self.emit_bytes(def_op, index as u16);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn statement(&mut self) -> anyhow::Result<()> {
|
|
||||||
if self.match_token(TokenType::Print) {
|
|
||||||
self.print_statement()
|
|
||||||
} else {
|
|
||||||
self.expression_statement()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn expression_statement(&mut self) -> anyhow::Result<()> {
|
|
||||||
debug!("expression statement");
|
|
||||||
self.expression(None)?;
|
|
||||||
self.emit_byte(OP_POP);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn print_statement(&mut self) -> anyhow::Result<()> {
|
|
||||||
self.expression(None)?;
|
|
||||||
self.consume(
|
|
||||||
TokenType::Eol,
|
|
||||||
"No further statements expected. Please start on a new line after the first one.\n",
|
|
||||||
)?;
|
|
||||||
self.emit_byte(OP_PRINT);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn advance(&mut self) -> anyhow::Result<()> {
|
|
||||||
if self.current < self.tokens.len() - 1 {
|
|
||||||
self.previous = self.current;
|
|
||||||
self.previous_token = &self.tokens[self.previous];
|
|
||||||
self.current += 1;
|
|
||||||
self.current_token = &self.tokens[self.current];
|
|
||||||
}
|
|
||||||
if let TokenType::Error = self.current_token.token_type {
|
|
||||||
self.had_error = true;
|
|
||||||
Err(anyhow!(
|
|
||||||
"Error at {} on line {}",
|
|
||||||
self.current_token.lexeme,
|
|
||||||
self.current_token.line
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn consume(&mut self, token_type: TokenType, message: &str) -> anyhow::Result<()> {
|
|
||||||
if token_type == self.current_token.token_type {
|
|
||||||
self.advance()
|
|
||||||
} else {
|
|
||||||
Err(anyhow!(
|
|
||||||
r#"{} at line {}: "{}""#,
|
|
||||||
message,
|
|
||||||
self.current_token.line + 1,
|
|
||||||
self.source[self.current_token.line]
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn match_token(&mut self, token_type: TokenType) -> bool {
|
|
||||||
if !self.check(token_type) {
|
|
||||||
false
|
|
||||||
} else {
|
|
||||||
self.advance().expect("token expected");
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check(&mut self, token_type: TokenType) -> bool {
|
|
||||||
self.current_token.token_type == token_type
|
|
||||||
}
|
|
||||||
|
|
||||||
fn expression(&mut self, expected_type: Option<TokenType>) -> anyhow::Result<()> {
|
|
||||||
self.parse_precedence(PREC_ASSIGNMENT, expected_type)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_precedence(
|
|
||||||
&mut self,
|
|
||||||
precedence: usize,
|
|
||||||
expected_type: Option<TokenType>,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
self.advance()?;
|
|
||||||
let rule = get_rule(&self.previous_token.token_type);
|
|
||||||
debug!("Precedence rule: {:?}", rule);
|
|
||||||
if let Some(prefix) = rule.prefix {
|
|
||||||
prefix(self, expected_type)?;
|
|
||||||
while precedence <= get_rule(&self.current_token.token_type).precedence {
|
|
||||||
self.advance()?;
|
|
||||||
let infix_rule = get_rule(&self.previous_token.token_type).infix;
|
|
||||||
if let Some(infix) = infix_rule {
|
|
||||||
infix(self, expected_type)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return Err(anyhow!("Expect expression."));
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn emit_byte(&mut self, byte: u16) {
|
|
||||||
self.chunk.add(byte, self.previous_token.line);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn emit_bytes(&mut self, b1: u16, b2: u16) {
|
|
||||||
self.emit_byte(b1);
|
|
||||||
self.emit_byte(b2);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn emit_constant(&mut self, value: Value) {
|
|
||||||
let index = self.chunk.add_constant(value);
|
|
||||||
self.emit_bytes(OP_CONSTANT, index as u16);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type ParseFn = fn(&mut Compiler, expected_type: Option<TokenType>) -> anyhow::Result<()>;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct Rule {
|
|
||||||
prefix: Option<ParseFn>,
|
|
||||||
infix: Option<ParseFn>,
|
|
||||||
precedence: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Rule {
|
|
||||||
fn new(prefix: Option<ParseFn>, infix: Option<ParseFn>, precedence: usize) -> Self {
|
|
||||||
Self {
|
|
||||||
prefix,
|
|
||||||
infix,
|
|
||||||
precedence,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn number(s: &mut Compiler, mut expected_type: Option<TokenType>) -> anyhow::Result<()> {
|
|
||||||
debug!("number: expected type {:?}", expected_type);
|
|
||||||
|
|
||||||
// coerce unknown numeric type to the expected type of the expression if any
|
|
||||||
if let None = expected_type {
|
|
||||||
if !s.typestack.is_empty() {
|
|
||||||
expected_type = Some(*s.typestack.last().unwrap());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let number = &s.previous_token.lexeme;
|
|
||||||
let value = if let Some(expected_type) = expected_type {
|
|
||||||
match expected_type {
|
|
||||||
TokenType::I32 => parse_num!(s, I32, number),
|
|
||||||
TokenType::I64 => parse_num!(s, I64, number),
|
|
||||||
TokenType::U32 => parse_num!(s, U32, number),
|
|
||||||
TokenType::U64 => parse_num!(s, U64, number),
|
|
||||||
TokenType::F32 => parse_num!(s, F32, number),
|
|
||||||
TokenType::F64 => parse_num!(s, F64, number),
|
|
||||||
|
|
||||||
_ => {
|
|
||||||
return Err(anyhow!(
|
|
||||||
"Invalid type: expected {} value, got {}({})",
|
|
||||||
expected_type,
|
|
||||||
&s.previous_token.token_type,
|
|
||||||
number
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
match s.previous_token.token_type {
|
|
||||||
TokenType::Integer => Value::I64(number.parse()?),
|
|
||||||
TokenType::FloatingPoint => Value::F64(number.parse()?),
|
|
||||||
_ => panic!("I did not think this would happen")
|
|
||||||
}
|
|
||||||
};
|
|
||||||
s.emit_constant(value);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn literal(s: &mut Compiler, expected_type: Option<TokenType>) -> anyhow::Result<()> {
|
|
||||||
let actual_type = &s.previous_token.token_type;
|
|
||||||
if let Some(expected_type) = expected_type {
|
|
||||||
match (actual_type, expected_type) {
|
|
||||||
(TokenType::False, TokenType::Bool) => {
|
|
||||||
s.typestack.push(TokenType::Bool);
|
|
||||||
s.emit_constant(Value::Bool(false))
|
|
||||||
}
|
|
||||||
(TokenType::True, TokenType::Bool) => {
|
|
||||||
s.typestack.push(TokenType::Bool);
|
|
||||||
s.emit_constant(Value::Bool(true))
|
|
||||||
}
|
|
||||||
(TokenType::StringType, TokenType::StringType) => {
|
|
||||||
s.typestack.push(TokenType::StringType);
|
|
||||||
s.emit_constant(Value::String(s.previous_token.lexeme.clone()))
|
|
||||||
}
|
|
||||||
//list
|
|
||||||
//map
|
|
||||||
//struct value
|
|
||||||
_ => {
|
|
||||||
return Err(anyhow!(
|
|
||||||
"Invalid type: expected {} value, got {}({})",
|
|
||||||
expected_type,
|
|
||||||
&s.previous_token.token_type,
|
|
||||||
s.previous_token.lexeme
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
match actual_type {
|
|
||||||
TokenType::False => s.emit_constant(Value::Bool(false)),
|
|
||||||
TokenType::True => s.emit_constant(Value::Bool(true)),
|
|
||||||
TokenType::StringType => s.emit_constant(Value::String(s.previous_token.lexeme.clone())),
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn skip(s: &mut Compiler, _expected_type: Option<TokenType>) -> anyhow::Result<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn grouping(s: &mut Compiler, _expected_type: Option<TokenType>) -> anyhow::Result<()> {
|
|
||||||
s.expression(None)?;
|
|
||||||
s.consume(TokenType::RightParen, "Expect ')' after expression.")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unary(s: &mut Compiler, expected_type: Option<TokenType>) -> anyhow::Result<()> {
|
|
||||||
let operator_type = s.previous_token.token_type;
|
|
||||||
|
|
||||||
s.parse_precedence(PREC_UNARY, None)?;
|
|
||||||
|
|
||||||
match operator_type {
|
|
||||||
TokenType::Minus => {
|
|
||||||
s.emit_byte(OP_NEGATE);
|
|
||||||
}
|
|
||||||
TokenType::Bang => {
|
|
||||||
s.emit_byte(OP_NOT);
|
|
||||||
}
|
|
||||||
_ => unimplemented!("unary other than ! and -"),
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn binary(s: &mut Compiler, expected_type: Option<TokenType>) -> anyhow::Result<()> {
|
|
||||||
let operator_type = &s.previous_token.token_type;
|
|
||||||
debug!("operator {:?}", operator_type);
|
|
||||||
debug!("expected type {:?}", expected_type);
|
|
||||||
let rule = get_rule(operator_type);
|
|
||||||
s.parse_precedence(rule.precedence + 1, None)?;
|
|
||||||
match operator_type {
|
|
||||||
TokenType::Plus => s.emit_byte(OP_ADD),
|
|
||||||
TokenType::Minus => s.emit_byte(OP_SUBTRACT),
|
|
||||||
TokenType::Star => s.emit_byte(OP_MULTIPLY),
|
|
||||||
TokenType::Slash => s.emit_byte(OP_DIVIDE),
|
|
||||||
TokenType::BitAnd => s.emit_byte(OP_BITAND),
|
|
||||||
TokenType::BitOr => s.emit_byte(OP_BITOR),
|
|
||||||
TokenType::BitXor => s.emit_byte(OP_BITXOR),
|
|
||||||
TokenType::GreaterGreater => s.emit_byte(OP_SHR),
|
|
||||||
TokenType::LessLess => s.emit_byte(OP_SHL),
|
|
||||||
TokenType::EqualEqual => s.emit_byte(OP_EQUAL),
|
|
||||||
TokenType::Greater => s.emit_byte(OP_GREATER),
|
|
||||||
TokenType::GreaterEqual => s.emit_byte(OP_GREATER_EQUAL),
|
|
||||||
TokenType::Less => s.emit_byte(OP_LESS),
|
|
||||||
TokenType::LessEqual => s.emit_byte(OP_LESS_EQUAL),
|
|
||||||
_ => unimplemented!("binary other than plus, minus, star, slash"),
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn variable(s: &mut Compiler, expected_type: Option<TokenType>) -> anyhow::Result<()> {
|
|
||||||
let index = s.identifier_constant(s.previous_token)?;
|
|
||||||
s.emit_bytes(OP_GET, index as u16);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_rule(operator_type: &TokenType) -> &'static Rule {
|
|
||||||
debug!("{:?}", operator_type);
|
|
||||||
RULES.get(operator_type).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
static RULES: LazyLock<HashMap<TokenType, Rule>> = LazyLock::new(|| {
|
|
||||||
let mut rules: HashMap<TokenType, Rule> = HashMap::new();
|
|
||||||
rules.insert(TokenType::Bang, Rule::new(Some(unary), None, PREC_UNARY));
|
|
||||||
rules.insert(TokenType::BangEqual, Rule::new(None, None, PREC_EQUALITY));
|
|
||||||
rules.insert(TokenType::BitOr, Rule::new(None, Some(binary), PREC_BITOR));
|
|
||||||
rules.insert(
|
|
||||||
TokenType::BitXor,
|
|
||||||
Rule::new(None, Some(binary), PREC_BITXOR),
|
|
||||||
);
|
|
||||||
rules.insert(TokenType::Colon, Rule::new(None, None, PREC_NONE));
|
|
||||||
rules.insert(TokenType::Comma, Rule::new(None, None, PREC_NONE));
|
|
||||||
rules.insert(TokenType::Date, Rule::new(None, None, PREC_NONE));
|
|
||||||
rules.insert(TokenType::Dot, Rule::new(None, None, PREC_NONE));
|
|
||||||
rules.insert(TokenType::Else, Rule::new(None, None, PREC_NONE));
|
|
||||||
rules.insert(TokenType::Eof, Rule::new(Some(skip), None, PREC_NONE));
|
|
||||||
rules.insert(TokenType::Eol, Rule::new(Some(skip), None, PREC_NONE));
|
|
||||||
rules.insert(TokenType::Equal, Rule::new(None, None, PREC_NONE));
|
|
||||||
rules.insert(TokenType::False, Rule::new(None, None, PREC_NONE));
|
|
||||||
rules.insert(TokenType::Fn, Rule::new(None, None, PREC_NONE));
|
|
||||||
rules.insert(
|
|
||||||
TokenType::EqualEqual,
|
|
||||||
Rule::new(None, Some(binary), PREC_EQUALITY),
|
|
||||||
);
|
|
||||||
rules.insert(TokenType::False, Rule::new(Some(literal), None, PREC_NONE));
|
|
||||||
rules.insert(TokenType::FloatingPoint, Rule::new(Some(number), None, PREC_NONE));
|
|
||||||
rules.insert(
|
|
||||||
TokenType::Greater,
|
|
||||||
Rule::new(None, Some(binary), PREC_COMPARISON),
|
|
||||||
);
|
|
||||||
rules.insert(
|
|
||||||
TokenType::GreaterEqual,
|
|
||||||
Rule::new(None, Some(binary), PREC_COMPARISON),
|
|
||||||
);
|
|
||||||
rules.insert(
|
|
||||||
TokenType::GreaterGreater,
|
|
||||||
Rule::new(None, Some(binary), PREC_BITSHIFT),
|
|
||||||
);
|
|
||||||
rules.insert(TokenType::I32, Rule::new(None, None, PREC_NONE));
|
|
||||||
rules.insert(TokenType::I64, Rule::new(None, None, PREC_NONE));
|
|
||||||
rules.insert(
|
|
||||||
TokenType::Identifier,
|
|
||||||
Rule::new(Some(variable), None, PREC_NONE),
|
|
||||||
);
|
|
||||||
rules.insert(TokenType::Integer, Rule::new(Some(number), None, PREC_NONE));
|
|
||||||
rules.insert(TokenType::Indent, Rule::new(Some(skip), None, PREC_NONE));
|
|
||||||
rules.insert(TokenType::LeftBrace, Rule::new(None, None, PREC_NONE));
|
|
||||||
rules.insert(TokenType::LeftBracket, Rule::new(None, None, PREC_NONE));
|
|
||||||
rules.insert(
|
|
||||||
TokenType::LeftParen,
|
|
||||||
Rule::new(Some(grouping), None, PREC_NONE),
|
|
||||||
);
|
|
||||||
rules.insert(
|
|
||||||
TokenType::Less,
|
|
||||||
Rule::new(None, Some(binary), PREC_COMPARISON),
|
|
||||||
);
|
|
||||||
rules.insert(
|
|
||||||
TokenType::LessEqual,
|
|
||||||
Rule::new(None, Some(binary), PREC_COMPARISON),
|
|
||||||
);
|
|
||||||
rules.insert(
|
|
||||||
TokenType::LessLess,
|
|
||||||
Rule::new(None, Some(binary), PREC_BITSHIFT),
|
|
||||||
);
|
|
||||||
rules.insert(
|
|
||||||
TokenType::LogicalAnd,
|
|
||||||
Rule::new(None, Some(binary), PREC_AND),
|
|
||||||
);
|
|
||||||
rules.insert(TokenType::LogicalOr, Rule::new(None, Some(binary), PREC_OR));
|
|
||||||
rules.insert(
|
|
||||||
TokenType::Minus,
|
|
||||||
Rule::new(Some(unary), Some(binary), PREC_TERM),
|
|
||||||
);
|
|
||||||
rules.insert(TokenType::Plus, Rule::new(None, Some(binary), PREC_TERM));
|
|
||||||
rules.insert(TokenType::Print, Rule::new(None, None, PREC_NONE));
|
|
||||||
rules.insert(TokenType::Return, Rule::new(None, None, PREC_NONE));
|
|
||||||
rules.insert(TokenType::RightParen, Rule::new(None, None, PREC_NONE));
|
|
||||||
rules.insert(TokenType::RightBrace, Rule::new(None, None, PREC_NONE));
|
|
||||||
rules.insert(TokenType::RightBracket, Rule::new(None, None, PREC_NONE));
|
|
||||||
rules.insert(TokenType::Slash, Rule::new(None, Some(binary), PREC_FACTOR));
|
|
||||||
rules.insert(TokenType::Star, Rule::new(None, Some(binary), PREC_FACTOR));
|
|
||||||
rules.insert(TokenType::StringType, Rule::new(Some(literal), None, PREC_NONE));
|
|
||||||
rules.insert(
|
|
||||||
TokenType::BitAnd,
|
|
||||||
Rule::new(None, Some(binary), PREC_BITAND),
|
|
||||||
);
|
|
||||||
rules.insert(TokenType::StringType, Rule::new(None, None, PREC_NONE));
|
|
||||||
rules.insert(TokenType::Struct, Rule::new(None, None, PREC_NONE));
|
|
||||||
rules.insert(TokenType::True, Rule::new(Some(literal), None, PREC_NONE));
|
|
||||||
rules.insert(TokenType::U32, Rule::new(None, None, PREC_NONE));
|
|
||||||
rules.insert(TokenType::U64, Rule::new(None, None, PREC_NONE));
|
|
||||||
rules.insert(TokenType::While, Rule::new(None, None, PREC_NONE));
|
|
||||||
|
|
||||||
rules
|
|
||||||
});
|
|
||||||
|
|
||||||
const PREC_NONE: usize = 0;
|
|
||||||
const PREC_ASSIGNMENT: usize = 1;
|
|
||||||
const PREC_OR: usize = 2;
|
|
||||||
const PREC_AND: usize = 3;
|
|
||||||
const PREC_BITAND: usize = 4;
|
|
||||||
const PREC_BITOR: usize = 5;
|
|
||||||
const PREC_BITXOR: usize = 6;
|
|
||||||
const PREC_EQUALITY: usize = 7;
|
|
||||||
const PREC_COMPARISON: usize = 8;
|
|
||||||
const PREC_BITSHIFT: usize = 9;
|
|
||||||
const PREC_TERM: usize = 10;
|
|
||||||
const PREC_FACTOR: usize = 11;
|
|
||||||
const PREC_UNARY: usize = 12;
|
|
||||||
const PREC_CALL: usize = 13;
|
|
||||||
const PREC_PRIMARY: usize = 14;
|
|
||||||
|
|
||||||
enum ValueType {
|
|
||||||
DateType,
|
|
||||||
BoolType,
|
|
||||||
CharType,
|
|
||||||
F32Type,
|
|
||||||
F64Type,
|
|
||||||
I32Type,
|
|
||||||
I64Type,
|
|
||||||
ObjectType,
|
|
||||||
U32Type,
|
|
||||||
U64Type,
|
|
||||||
StringType,
|
|
||||||
ListType,
|
|
||||||
MapType,
|
|
||||||
}
|
|
||||||
11
src/lib.rs
11
src/lib.rs
|
|
@ -1,9 +1,8 @@
|
||||||
pub mod chunk;
|
|
||||||
pub mod compiler;
|
|
||||||
mod keywords;
|
|
||||||
pub mod scanner;
|
pub mod scanner;
|
||||||
mod tokens;
|
|
||||||
mod value;
|
|
||||||
pub mod vm;
|
|
||||||
pub mod ast_compiler;
|
pub mod ast_compiler;
|
||||||
pub mod bytecode_compiler;
|
pub mod bytecode_compiler;
|
||||||
|
pub mod vm;
|
||||||
|
mod chunk;
|
||||||
|
mod keywords;
|
||||||
|
mod tokens;
|
||||||
|
mod value;
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,10 @@ fn main() -> anyhow::Result<()> {
|
||||||
|
|
||||||
let tokens = scan(
|
let tokens = scan(
|
||||||
r#"
|
r#"
|
||||||
fn main(a: string) -> u32:
|
fn main(a: list) -> list:
|
||||||
a + 42
|
a + 42
|
||||||
let text = "hello "
|
|
||||||
|
let text:list = ["hello "]
|
||||||
main(text)"#,
|
main(text)"#,
|
||||||
);
|
);
|
||||||
println!("{:?}", tokens);
|
println!("{:?}", tokens);
|
||||||
|
|
|
||||||
53
src/value.rs
53
src/value.rs
|
|
@ -2,7 +2,7 @@ use anyhow::anyhow;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt::{Display, Formatter, write};
|
use std::fmt::{Display, Formatter};
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Not, Shl, Shr, Sub};
|
use std::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Not, Shl, Shr, Sub};
|
||||||
|
|
||||||
|
|
@ -114,18 +114,6 @@ impl Into<Value> for DateTime<Utc> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<Value> for Vec<Value> {
|
|
||||||
fn into(self) -> Value {
|
|
||||||
Value::List(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<Value> for HashMap<Value, Value> {
|
|
||||||
fn into(self) -> Value {
|
|
||||||
Value::Map(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Value {
|
impl Display for Value {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
match &self {
|
match &self {
|
||||||
|
|
@ -167,6 +155,15 @@ impl Add<&Value> for &Value {
|
||||||
type Output = anyhow::Result<Value>;
|
type Output = anyhow::Result<Value>;
|
||||||
|
|
||||||
fn add(self, rhs: &Value) -> Self::Output {
|
fn add(self, rhs: &Value) -> Self::Output {
|
||||||
|
if let Value::List(s) = self {
|
||||||
|
let mut copy = s.clone();
|
||||||
|
copy.push(rhs.clone());
|
||||||
|
Ok(Value::List(copy))
|
||||||
|
} else if let Value::List(rhs) = rhs {
|
||||||
|
let mut copy = rhs.clone();
|
||||||
|
copy.push(self.clone());
|
||||||
|
Ok(Value::List(copy))
|
||||||
|
} else {
|
||||||
match (self, rhs) {
|
match (self, rhs) {
|
||||||
(Value::I32(a), Value::I32(b)) => Ok(Value::I32(a + b)),
|
(Value::I32(a), Value::I32(b)) => Ok(Value::I32(a + b)),
|
||||||
(Value::I64(a), Value::I64(b)) => Ok(Value::I64(a + b)),
|
(Value::I64(a), Value::I64(b)) => Ok(Value::I64(a + b)),
|
||||||
|
|
@ -182,14 +179,16 @@ impl Add<&Value> for &Value {
|
||||||
(Value::String(s), Value::F64(f)) => Ok(Value::String(format!("{}{}", s, f))),
|
(Value::String(s), Value::F64(f)) => Ok(Value::String(format!("{}{}", s, f))),
|
||||||
(Value::String(s), Value::Bool(b)) => Ok(Value::String(format!("{}{}", s, b))),
|
(Value::String(s), Value::Bool(b)) => Ok(Value::String(format!("{}{}", s, b))),
|
||||||
(Value::String(s), Value::Char(c)) => Ok(Value::String(format!("{}{}", s, c))),
|
(Value::String(s), Value::Char(c)) => Ok(Value::String(format!("{}{}", s, c))),
|
||||||
(Value::String(s1), Value::String(s2)) => Ok(Value::String(format!("{}{}", s1, s2))),
|
(Value::String(s1), Value::String(s2)) => {
|
||||||
(Value::String(s1), Value::List(l)) => Ok(Value::String(format!("{}{:?}", s1, l))),
|
Ok(Value::String(format!("{}{}", s1, s2)))
|
||||||
|
}
|
||||||
(Value::String(s1), Value::Map(m)) => Ok(Value::String(format!("{}{:?}", s1, m))),
|
(Value::String(s1), Value::Map(m)) => Ok(Value::String(format!("{}{:?}", s1, m))),
|
||||||
//enum?
|
//enum?
|
||||||
_ => Err(anyhow!("Cannot add")),
|
_ => Err(anyhow!("Cannot add")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Sub<&Value> for &Value {
|
impl Sub<&Value> for &Value {
|
||||||
type Output = anyhow::Result<Value>;
|
type Output = anyhow::Result<Value>;
|
||||||
|
|
@ -333,18 +332,18 @@ impl PartialEq for Value {
|
||||||
(Value::String(a), Value::String(b)) => a == b,
|
(Value::String(a), Value::String(b)) => a == b,
|
||||||
(Value::Char(a), Value::Char(b)) => a == b,
|
(Value::Char(a), Value::Char(b)) => a == b,
|
||||||
(Value::Date(a), Value::Date(b)) => a == b,
|
(Value::Date(a), Value::Date(b)) => a == b,
|
||||||
(Value::List(a), Value::List(b)) => a == b,
|
// (Value::List(a), Value::List(b)) => a == b,
|
||||||
(Value::Map(a), Value::Map(b)) => {
|
// (Value::Map(a), Value::Map(b)) => {
|
||||||
let mut equal = true;
|
// let mut equal = true;
|
||||||
for (k, v) in a.iter() {
|
// for (k, v) in a.iter() {
|
||||||
if !b.contains_key(k) || b.get(k).unwrap() != v {
|
// if !b.contains_key(k) || b.get(k).unwrap() != v {
|
||||||
//safe unwrap
|
// //safe unwrap
|
||||||
equal = false;
|
// equal = false;
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
equal
|
// equal
|
||||||
}
|
// }
|
||||||
// struct?
|
// struct?
|
||||||
_ => false, //?
|
_ => false, //?
|
||||||
}
|
}
|
||||||
|
|
|
||||||
16
src/vm.rs
16
src/vm.rs
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::chunk::Chunk;
|
use crate::chunk::Chunk;
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
|
use bumpalo::Bump;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
|
|
@ -24,6 +25,7 @@ pub struct Vm {
|
||||||
stack: Vec<Value>,
|
stack: Vec<Value>,
|
||||||
local_vars: HashMap<String, Value>,
|
local_vars: HashMap<String, Value>,
|
||||||
error_occurred: bool,
|
error_occurred: bool,
|
||||||
|
arena: Bump,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn interpret(chunk: &Chunk) -> anyhow::Result<Value> {
|
pub fn interpret(chunk: &Chunk) -> anyhow::Result<Value> {
|
||||||
|
|
@ -32,6 +34,7 @@ pub fn interpret(chunk: &Chunk) -> anyhow::Result<Value> {
|
||||||
stack: vec![],
|
stack: vec![],
|
||||||
local_vars: HashMap::new(),
|
local_vars: HashMap::new(),
|
||||||
error_occurred: false,
|
error_occurred: false,
|
||||||
|
arena: Bump::new(),
|
||||||
};
|
};
|
||||||
vm.run(chunk, vec![])
|
vm.run(chunk, vec![])
|
||||||
}
|
}
|
||||||
|
|
@ -42,6 +45,7 @@ pub fn interpret_function(chunk: &Chunk, args: Vec<Value>) -> anyhow::Result<Val
|
||||||
stack: vec![],
|
stack: vec![],
|
||||||
local_vars: HashMap::new(),
|
local_vars: HashMap::new(),
|
||||||
error_occurred: false,
|
error_occurred: false,
|
||||||
|
arena: Bump::new(),
|
||||||
};
|
};
|
||||||
vm.run(chunk, args)
|
vm.run(chunk, args)
|
||||||
}
|
}
|
||||||
|
|
@ -121,7 +125,16 @@ impl Vm {
|
||||||
OP_DEF_CHAR => define_var!(self, Char, chunk),
|
OP_DEF_CHAR => define_var!(self, Char, chunk),
|
||||||
OP_DEF_BOOL => define_var!(self, Bool, chunk),
|
OP_DEF_BOOL => define_var!(self, Bool, chunk),
|
||||||
OP_DEF_DATE => define_var!(self, Date, chunk),
|
OP_DEF_DATE => define_var!(self, Date, chunk),
|
||||||
OP_DEF_LIST => define_var!(self, List, chunk),
|
OP_DEF_LIST => {
|
||||||
|
let name = self.read_name(chunk);
|
||||||
|
let len = self.read(chunk);
|
||||||
|
let mut list = vec![];
|
||||||
|
for _ in 0..len {
|
||||||
|
let value = self.pop();
|
||||||
|
list.push(value);
|
||||||
|
}
|
||||||
|
self.local_vars.insert(name, Value::List(list));
|
||||||
|
}
|
||||||
OP_DEF_MAP => define_var!(self, Map, chunk),
|
OP_DEF_MAP => define_var!(self, Map, chunk),
|
||||||
OP_DEF_STRUCT => define_var!(self, Struct, chunk),
|
OP_DEF_STRUCT => define_var!(self, Struct, chunk),
|
||||||
OP_GET => {
|
OP_GET => {
|
||||||
|
|
@ -236,3 +249,4 @@ pub const OP_DEF_MAP: u16 = 37;
|
||||||
pub const OP_DEF_STRUCT: u16 = 38;
|
pub const OP_DEF_STRUCT: u16 = 38;
|
||||||
pub const OP_DEF_F32: u16 = 39;
|
pub const OP_DEF_F32: u16 = 39;
|
||||||
pub const OP_DEF_F64: u16 = 40;
|
pub const OP_DEF_F64: u16 = 40;
|
||||||
|
// pub const OP_NEW_LIST: u16 = 40;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue