tests
This commit is contained in:
parent
90ad226785
commit
3118ce97b0
12 changed files with 197 additions and 90 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
|
@ -124,9 +124,6 @@ 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"
|
||||||
|
|
@ -200,7 +197,6 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"axum",
|
"axum",
|
||||||
"bumpalo",
|
|
||||||
"chrono",
|
"chrono",
|
||||||
"dotenv",
|
"dotenv",
|
||||||
"log",
|
"log",
|
||||||
|
|
|
||||||
|
|
@ -20,5 +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"] }
|
|
||||||
walkdir = "2.5.0"
|
walkdir = "2.5.0"
|
||||||
|
|
|
||||||
|
|
@ -100,9 +100,9 @@ And I cherry picked things I like, mostly from rust and python.
|
||||||
```
|
```
|
||||||
let a = 42
|
let a = 42
|
||||||
```
|
```
|
||||||
* declares a variable of type i64
|
* declares a variable of type i64 (signed 64 bit integer)
|
||||||
|
|
||||||
or explictly as u32
|
or explictly as u32 (unsigned 32 bit integer)
|
||||||
```
|
```
|
||||||
let a:u32 = 42
|
let a:u32 = 42
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,7 @@
|
||||||
use crate::tokens::TokenType::{
|
use crate::tokens::TokenType::{Bang, Bool, 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, RightBracket, RightParen, SignedInteger, SingleRightArrow, Slash, Star, StringType, True, U32, U64, UnsignedInteger, Char};
|
||||||
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,
|
|
||||||
RightBracket, RightParen, SingleRightArrow, Slash, Star, StringType, True, U32, U64,
|
|
||||||
};
|
|
||||||
use crate::tokens::{Token, TokenType};
|
use crate::tokens::{Token, TokenType};
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
|
use anyhow::anyhow;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
|
@ -15,7 +11,7 @@ pub fn compile(tokens: Vec<Token>) -> anyhow::Result<Vec<Statement>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) struct Function {
|
pub struct Function {
|
||||||
pub(crate) name: Token,
|
pub(crate) name: Token,
|
||||||
pub(crate) parameters: Vec<Parameter>,
|
pub(crate) parameters: Vec<Parameter>,
|
||||||
pub(crate) return_type: TokenType,
|
pub(crate) return_type: TokenType,
|
||||||
|
|
@ -201,11 +197,13 @@ impl AstCompiler {
|
||||||
let var_type = match calculate_type(declared_type, inferred_type) {
|
let var_type = match calculate_type(declared_type, inferred_type) {
|
||||||
Ok(var_type) => var_type,
|
Ok(var_type) => var_type,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("error at line {}", name_token.line);
|
|
||||||
self.had_error = true;
|
self.had_error = true;
|
||||||
return Err(e);
|
return Err(anyhow!("error at line {}: {}", name_token.line, e));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
// match var_type{
|
||||||
|
// U32 => U32()
|
||||||
|
// }
|
||||||
self.vars.push(Expression::Variable {
|
self.vars.push(Expression::Variable {
|
||||||
name: name_token.lexeme.to_string(),
|
name: name_token.lexeme.to_string(),
|
||||||
var_type,
|
var_type,
|
||||||
|
|
@ -246,7 +244,7 @@ impl AstCompiler {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn or(&mut self) -> anyhow::Result<Expression> {
|
fn or(&mut self) -> anyhow::Result<Expression> {
|
||||||
let mut expr = self.and()?;
|
let expr = self.and()?;
|
||||||
self.binary(vec![TokenType::LogicalOr], expr)
|
self.binary(vec![TokenType::LogicalOr], expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -281,7 +279,7 @@ impl AstCompiler {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bitshift(&mut self) -> anyhow::Result<Expression> {
|
fn bitshift(&mut self) -> anyhow::Result<Expression> {
|
||||||
let mut expr = self.term()?;
|
let expr = self.term()?;
|
||||||
self.binary(vec![GreaterGreater, LessLess], expr)
|
self.binary(vec![GreaterGreater, LessLess], expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -361,6 +359,12 @@ impl AstCompiler {
|
||||||
literaltype: StringType,
|
literaltype: StringType,
|
||||||
value: Value::String(self.previous().lexeme.to_string()),
|
value: Value::String(self.previous().lexeme.to_string()),
|
||||||
}
|
}
|
||||||
|
} else if self.match_token(vec![Char]) {
|
||||||
|
Expression::Literal {
|
||||||
|
line: self.peek().line,
|
||||||
|
literaltype: Char,
|
||||||
|
value: Value::Char(self.previous().lexeme.chars().next().unwrap()),
|
||||||
|
}
|
||||||
} else if self.match_token(vec![LeftParen]) {
|
} else if self.match_token(vec![LeftParen]) {
|
||||||
let expr = self.expression()?;
|
let expr = self.expression()?;
|
||||||
self.consume(RightParen, "Expect ')' after expression.")?;
|
self.consume(RightParen, "Expect ')' after expression.")?;
|
||||||
|
|
@ -418,7 +422,6 @@ impl AstCompiler {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn function_call(&mut self, name: String) -> anyhow::Result<Expression> {
|
fn function_call(&mut self, name: String) -> anyhow::Result<Expression> {
|
||||||
println!("function call {}", name);
|
|
||||||
let function_name = self.functions.get(&name).unwrap().name.lexeme.clone();
|
let function_name = self.functions.get(&name).unwrap().name.lexeme.clone();
|
||||||
let function = self.functions.get(&function_name).unwrap().clone();
|
let function = self.functions.get(&function_name).unwrap().clone();
|
||||||
|
|
||||||
|
|
@ -509,20 +512,20 @@ 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
|
|
||||||
);
|
|
||||||
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, //need this?
|
||||||
|
(I32, Integer) => I32,
|
||||||
(U32, U64) => U32,
|
(U32, U64) => U32,
|
||||||
|
(U32, Integer) => U32,
|
||||||
(F32, F64) => F32,
|
(F32, F64) => F32,
|
||||||
|
(F32, FloatingPoint) => F32,
|
||||||
(F64, I64) => F64,
|
(F64, I64) => F64,
|
||||||
|
(F64, FloatingPoint) => F64,
|
||||||
(U64, I64) => U64,
|
(U64, I64) => U64,
|
||||||
(U64, I32) => U64,
|
(U64, I32) => U64,
|
||||||
(StringType, _) => StringType, // meh, this all needs rigorous testing
|
(StringType, _) => StringType, // meh, this all needs rigorous testing. Update: this is in progress
|
||||||
_ => {
|
_ => {
|
||||||
return Err(anyhow::anyhow!(
|
return Err(anyhow::anyhow!(
|
||||||
"Incompatible types. Expected {}, found {}",
|
"Incompatible types. Expected {}, found {}",
|
||||||
|
|
@ -697,7 +700,16 @@ impl Expression {
|
||||||
Self::Grouping { 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::List { literaltype, .. } => literaltype.clone(),
|
||||||
Self::Unary { right, .. } => right.infer_type(),
|
Self::Unary {
|
||||||
|
right, operator, ..
|
||||||
|
} => {
|
||||||
|
let literal_type = right.infer_type();
|
||||||
|
if literal_type == Integer && operator.token_type == Minus {
|
||||||
|
SignedInteger
|
||||||
|
} else {
|
||||||
|
UnsignedInteger
|
||||||
|
}
|
||||||
|
}
|
||||||
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(),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ use crate::vm::{
|
||||||
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_PRINT,
|
||||||
OP_RETURN, OP_SHL, OP_SHR, OP_SUBTRACT,
|
OP_RETURN, OP_SHL, OP_SHR, OP_SUBTRACT,
|
||||||
};
|
};
|
||||||
use anyhow::anyhow;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
pub fn compile(
|
pub fn compile(
|
||||||
|
|
@ -47,7 +46,7 @@ pub(crate) fn compile_name(
|
||||||
|
|
||||||
struct Compiler {
|
struct Compiler {
|
||||||
chunk: Chunk,
|
chunk: Chunk,
|
||||||
had_error: bool,
|
_had_error: bool,
|
||||||
current_line: usize,
|
current_line: usize,
|
||||||
vars: HashMap<String, usize>,
|
vars: HashMap<String, usize>,
|
||||||
}
|
}
|
||||||
|
|
@ -56,7 +55,7 @@ impl Compiler {
|
||||||
fn new(name: &str) -> Self {
|
fn new(name: &str) -> Self {
|
||||||
Self {
|
Self {
|
||||||
chunk: Chunk::new(name),
|
chunk: Chunk::new(name),
|
||||||
had_error: false,
|
_had_error: false,
|
||||||
current_line: 0,
|
current_line: 0,
|
||||||
vars: HashMap::new(),
|
vars: HashMap::new(),
|
||||||
}
|
}
|
||||||
|
|
@ -142,7 +141,6 @@ impl Compiler {
|
||||||
} else {
|
} else {
|
||||||
name.clone()
|
name.clone()
|
||||||
};
|
};
|
||||||
println!("call {}", name);
|
|
||||||
let name_index = self
|
let name_index = self
|
||||||
.chunk
|
.chunk
|
||||||
.find_constant(&name)
|
.find_constant(&name)
|
||||||
|
|
|
||||||
94
src/compiler_tests.rs
Normal file
94
src/compiler_tests.rs
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::compile;
|
||||||
|
use crate::scanner::scan;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn literal_int() {
|
||||||
|
assert!(compile("1").is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn literal_float() {
|
||||||
|
assert!(compile("2.1").is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn literal_float_scientific() {
|
||||||
|
assert!(compile("2.1e5").is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn literal_string() {
|
||||||
|
assert!(compile(r#""a""#).is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn literal_list() {
|
||||||
|
assert!(compile(r#"["abc","def"]"#).is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn let_infer_type() {
|
||||||
|
assert!(compile(r#"let a=1"#).is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn let_u32() {
|
||||||
|
assert!(compile(r#"let a:u32=1"#).is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn let_char() {
|
||||||
|
assert!(scan(r#"let a:char='a'"#).is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn let_u32_invalid_value_negative() {
|
||||||
|
let r = compile("let a:u32=-1");
|
||||||
|
assert!(r.is_err());
|
||||||
|
if let Err(e) = &r {
|
||||||
|
assert_eq!(
|
||||||
|
e.to_string(),
|
||||||
|
"error at line 1: Incompatible types. Expected u32, found i32/64"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn let_u64_invalid_value_negative() {
|
||||||
|
let r = compile("let a:u64=-1");
|
||||||
|
assert!(r.is_err());
|
||||||
|
if let Err(e) = &r {
|
||||||
|
assert_eq!(
|
||||||
|
e.to_string(),
|
||||||
|
"error at line 1: Incompatible types. Expected u64, found i32/64"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn let_u64_invalid_value_string() {
|
||||||
|
let r = compile(r#"let a:u64="not ok""#);
|
||||||
|
assert!(r.is_err());
|
||||||
|
if let Err(e) = &r {
|
||||||
|
assert_eq!(
|
||||||
|
e.to_string(),
|
||||||
|
"error at line 1: Incompatible types. Expected u64, found string"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn call_fn_with_args_returns_value() {
|
||||||
|
assert!(
|
||||||
|
compile(
|
||||||
|
r#"
|
||||||
|
fn hello(name: string) -> string:
|
||||||
|
"Hello " + name
|
||||||
|
hello("world")"#
|
||||||
|
)
|
||||||
|
.is_ok()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
16
src/lib.rs
16
src/lib.rs
|
|
@ -1,8 +1,20 @@
|
||||||
pub mod scanner;
|
use std::collections::HashMap;
|
||||||
|
use crate::scanner::scan;
|
||||||
|
|
||||||
pub mod ast_compiler;
|
pub mod ast_compiler;
|
||||||
pub mod bytecode_compiler;
|
pub mod bytecode_compiler;
|
||||||
pub mod vm;
|
|
||||||
pub mod chunk;
|
pub mod chunk;
|
||||||
mod keywords;
|
mod keywords;
|
||||||
|
pub mod scanner;
|
||||||
|
mod compiler_tests;
|
||||||
mod tokens;
|
mod tokens;
|
||||||
mod value;
|
mod value;
|
||||||
|
pub mod vm;
|
||||||
|
|
||||||
|
pub fn compile(src: &str) -> anyhow::Result<chunk::Chunk> {
|
||||||
|
let tokens = scan(src)?;
|
||||||
|
let mut registry = HashMap::new();
|
||||||
|
let ast= ast_compiler::compile(tokens)?;
|
||||||
|
let bytecode = bytecode_compiler::compile("", &ast, &mut registry)?;
|
||||||
|
Ok(bytecode)
|
||||||
|
}
|
||||||
|
|
|
||||||
37
src/main.rs
37
src/main.rs
|
|
@ -24,7 +24,7 @@ async fn main() -> anyhow::Result<()> {
|
||||||
if path.is_file() && path.ends_with("web.crud") {
|
if path.is_file() && path.ends_with("web.crud") {
|
||||||
print!("compiling {:?}: ", path);
|
print!("compiling {:?}: ", path);
|
||||||
let source = fs::read_to_string(path)?;
|
let source = fs::read_to_string(path)?;
|
||||||
let tokens = scan(&source);
|
let tokens = scan(&source)?;
|
||||||
match ast_compiler::compile(tokens) {
|
match ast_compiler::compile(tokens) {
|
||||||
Ok(statements) => {
|
Ok(statements) => {
|
||||||
let path = path
|
let path = path
|
||||||
|
|
@ -53,7 +53,10 @@ async fn main() -> anyhow::Result<()> {
|
||||||
registry: registry.clone(),
|
registry: registry.clone(),
|
||||||
});
|
});
|
||||||
println!("adding {}", path);
|
println!("adding {}", path);
|
||||||
app = app.route(&format!("/{}",path.replace("/web", "")), get(handle_get).with_state(state.clone()));
|
app = app.route(
|
||||||
|
&format!("/{}", path.replace("/web", "")),
|
||||||
|
get(handle_get).with_state(state.clone()),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?;
|
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?;
|
||||||
println!("listening on {}", listener.local_addr()?);
|
println!("listening on {}", listener.local_addr()?);
|
||||||
|
|
@ -77,33 +80,3 @@ async fn handle_get(State(state): State<Arc<AppState>>) -> Result<Json<String>,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn test_compile() -> anyhow::Result<()> {
|
|
||||||
let tokens = scan(
|
|
||||||
r#"
|
|
||||||
fn hello(name: string) -> string:
|
|
||||||
"Hello "+name
|
|
||||||
hello("sander")"#,
|
|
||||||
);
|
|
||||||
let mut registry = HashMap::new();
|
|
||||||
match ast_compiler::compile(tokens) {
|
|
||||||
Ok(statements) => {
|
|
||||||
println!("{:?}", statements);
|
|
||||||
let chunk = compile("", &statements, &mut registry)?;
|
|
||||||
chunk.disassemble();
|
|
||||||
// println!("{}", interpret(&chunk).await?);
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
println!("{}", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
println!("{:?}", registry);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
use anyhow::anyhow;
|
||||||
use crate::tokens::TokenType::{BitXor, FloatingPoint, Integer};
|
use crate::tokens::TokenType::{BitXor, FloatingPoint, Integer};
|
||||||
use crate::{
|
use crate::{
|
||||||
keywords,
|
keywords,
|
||||||
|
|
@ -7,7 +8,7 @@ use crate::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn scan(source: &str) -> Vec<Token> {
|
pub fn scan(source: &str) -> anyhow::Result<Vec<Token>> {
|
||||||
let scanner = Scanner {
|
let scanner = Scanner {
|
||||||
chars: source.chars().collect(),
|
chars: source.chars().collect(),
|
||||||
current: 0,
|
current: 0,
|
||||||
|
|
@ -20,17 +21,17 @@ pub fn scan(source: &str) -> Vec<Token> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Scanner {
|
impl Scanner {
|
||||||
fn scan(mut self) -> Vec<Token> {
|
fn scan(mut self) -> anyhow::Result<Vec<Token>> {
|
||||||
while !self.is_at_end() {
|
while !self.is_at_end() {
|
||||||
self.start = self.current;
|
self.start = self.current;
|
||||||
self.scan_token();
|
self.scan_token()?;
|
||||||
}
|
}
|
||||||
self.add_token(TokenType::Eol);
|
self.add_token(TokenType::Eol);
|
||||||
self.add_token(TokenType::Eof);
|
self.add_token(TokenType::Eof);
|
||||||
self.tokens
|
Ok(self.tokens)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scan_token(&mut self) {
|
fn scan_token(&mut self) -> anyhow::Result<()>{
|
||||||
let c = self.advance();
|
let c = self.advance();
|
||||||
if self.new_line && (c == ' ' || c == '\t') {
|
if self.new_line && (c == ' ' || c == '\t') {
|
||||||
self.add_token(TokenType::Indent);
|
self.add_token(TokenType::Indent);
|
||||||
|
|
@ -106,7 +107,8 @@ impl Scanner {
|
||||||
self.add_token(TokenType::Slash);
|
self.add_token(TokenType::Slash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
'"' => self.string(),
|
'\'' => self.char()?,
|
||||||
|
'"' => self.string()?,
|
||||||
'\r' | '\t' | ' ' => {}
|
'\r' | '\t' | ' ' => {}
|
||||||
'\n' => {
|
'\n' => {
|
||||||
self.line += 1;
|
self.line += 1;
|
||||||
|
|
@ -136,11 +138,12 @@ impl Scanner {
|
||||||
} else if is_alpha(c) {
|
} else if is_alpha(c) {
|
||||||
self.identifier();
|
self.identifier();
|
||||||
} else {
|
} else {
|
||||||
println!("Unexpected identifier at line {}", self.line);
|
return Err(anyhow!("Unexpected identifier at line {}", self.line));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn identifier(&mut self) {
|
fn identifier(&mut self) {
|
||||||
|
|
@ -163,14 +166,35 @@ impl Scanner {
|
||||||
self.advance();
|
self.advance();
|
||||||
}
|
}
|
||||||
|
|
||||||
while is_digit(self.peek()) {
|
while is_digit_or_scientific(self.peek()) {
|
||||||
self.advance();
|
self.advance();
|
||||||
}
|
}
|
||||||
let value: String = self.chars[self.start..self.current].iter().collect();
|
let value: String = self.chars[self.start..self.current].iter().collect();
|
||||||
self.add_token_with_value(if has_dot { FloatingPoint } else { Integer }, value);
|
self.add_token_with_value(if has_dot { FloatingPoint } else { Integer }, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn string(&mut self) {
|
fn char(&mut self) -> anyhow::Result<()>{
|
||||||
|
while self.peek() != '\'' && !self.is_at_end() {
|
||||||
|
self.advance();
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.is_at_end() {
|
||||||
|
return Err(anyhow!("Unterminated char at {}", self.line))
|
||||||
|
}
|
||||||
|
|
||||||
|
self.advance();
|
||||||
|
|
||||||
|
let value: String = self.chars[self.start + 1..self.current - 1]
|
||||||
|
.iter()
|
||||||
|
.collect();
|
||||||
|
if value.len() != 1 {
|
||||||
|
return Err(anyhow!("Illegal char length for {} at line {}", value, self.line))
|
||||||
|
}
|
||||||
|
self.add_token_with_value(TokenType::Char, value);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn string(&mut self) -> anyhow::Result<()>{
|
||||||
while self.peek() != '"' && !self.is_at_end() {
|
while self.peek() != '"' && !self.is_at_end() {
|
||||||
if self.peek() == '\n' {
|
if self.peek() == '\n' {
|
||||||
self.line += 1;
|
self.line += 1;
|
||||||
|
|
@ -179,7 +203,7 @@ impl Scanner {
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.is_at_end() {
|
if self.is_at_end() {
|
||||||
println!("Unterminated string at {}", self.line)
|
return Err(anyhow!("Unterminated string at {}", self.line))
|
||||||
}
|
}
|
||||||
|
|
||||||
self.advance();
|
self.advance();
|
||||||
|
|
@ -188,6 +212,7 @@ impl Scanner {
|
||||||
.iter()
|
.iter()
|
||||||
.collect();
|
.collect();
|
||||||
self.add_token_with_value(TokenType::StringType, value);
|
self.add_token_with_value(TokenType::StringType, value);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn peek(&self) -> char {
|
fn peek(&self) -> char {
|
||||||
|
|
@ -244,6 +269,10 @@ fn is_digit(c: char) -> bool {
|
||||||
c >= '0' && c <= '9'
|
c >= '0' && c <= '9'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_digit_or_scientific(c: char) -> bool {
|
||||||
|
is_digit(c) || c=='e' || c=='E'
|
||||||
|
}
|
||||||
|
|
||||||
fn is_alphanumeric(c: char) -> bool {
|
fn is_alphanumeric(c: char) -> bool {
|
||||||
is_alpha(c) || is_digit(c)
|
is_alpha(c) || is_digit(c)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,6 @@ impl Token {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
enum Value {
|
|
||||||
None,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Copy, Hash)]
|
#[derive(Debug, PartialEq, Clone, Copy, Hash)]
|
||||||
pub enum TokenType {
|
pub enum TokenType {
|
||||||
Bang,
|
Bang,
|
||||||
|
|
@ -55,7 +50,9 @@ pub enum TokenType {
|
||||||
Identifier,
|
Identifier,
|
||||||
If,
|
If,
|
||||||
Indent,
|
Indent,
|
||||||
Integer, //undetermined integer type
|
Integer,
|
||||||
|
SignedInteger,
|
||||||
|
UnsignedInteger,
|
||||||
LeftBrace,
|
LeftBrace,
|
||||||
LeftBracket,
|
LeftBracket,
|
||||||
LeftParen,
|
LeftParen,
|
||||||
|
|
@ -69,7 +66,7 @@ pub enum TokenType {
|
||||||
LogicalOr,
|
LogicalOr,
|
||||||
Minus,
|
Minus,
|
||||||
Not,
|
Not,
|
||||||
FloatingPoint, //undetermined float type
|
FloatingPoint,
|
||||||
Object,
|
Object,
|
||||||
Plus,
|
Plus,
|
||||||
Print,
|
Print,
|
||||||
|
|
@ -157,6 +154,8 @@ impl fmt::Display for TokenType {
|
||||||
TokenType::True => write!(f, "true"),
|
TokenType::True => write!(f, "true"),
|
||||||
TokenType::Void => write!(f, "()"),
|
TokenType::Void => write!(f, "()"),
|
||||||
TokenType::While => write!(f, "while"),
|
TokenType::While => write!(f, "while"),
|
||||||
|
TokenType::SignedInteger => write!(f, "i32/64"),
|
||||||
|
TokenType::UnsignedInteger => write!(f, "u32/64"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -143,7 +143,7 @@ impl Display for Value {
|
||||||
&Value::Enum => write!(f, "enum"),
|
&Value::Enum => write!(f, "enum"),
|
||||||
&Value::Struct(v) => write!(f, "{}", v),
|
&Value::Struct(v) => write!(f, "{}", v),
|
||||||
&Value::List(v) => write!(f, "{:?}", v),
|
&Value::List(v) => write!(f, "{:?}", v),
|
||||||
&Value::Map(v) => write!(f, "map"),
|
&Value::Map(_) => write!(f, "map"),
|
||||||
&Value::Error(v) => write!(f, "{}", v),
|
&Value::Error(v) => write!(f, "{}", v),
|
||||||
&Value::Void => write!(f, "()"),
|
&Value::Void => write!(f, "()"),
|
||||||
}
|
}
|
||||||
|
|
@ -295,7 +295,7 @@ impl Not for &Value {
|
||||||
type Output = anyhow::Result<Value>;
|
type Output = anyhow::Result<Value>;
|
||||||
|
|
||||||
fn not(self) -> Self::Output {
|
fn not(self) -> Self::Output {
|
||||||
match (self) {
|
match self {
|
||||||
Value::Bool(b) => Ok(Value::Bool(!b)),
|
Value::Bool(b) => Ok(Value::Bool(!b)),
|
||||||
Value::I32(i32) => Ok(Value::I32(!i32)),
|
Value::I32(i32) => Ok(Value::I32(!i32)),
|
||||||
Value::I64(i64) => Ok(Value::I64(!i64)),
|
Value::I64(i64) => Ok(Value::I64(!i64)),
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
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;
|
||||||
|
|
||||||
|
|
@ -175,10 +174,6 @@ impl <'a> Vm<'a> {
|
||||||
chunk.constants[index].to_string() //string??
|
chunk.constants[index].to_string() //string??
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reset_stack(&mut self) {
|
|
||||||
self.stack.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn push(&mut self, value: Value) {
|
fn push(&mut self, value: Value) {
|
||||||
self.stack.push(value);
|
self.stack.push(value);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue