first step in type checking

This commit is contained in:
Shautvast 2025-10-22 16:14:38 +02:00
parent 1d58725559
commit e2ddf94a00
7 changed files with 245 additions and 63 deletions

View file

@ -3,7 +3,7 @@ use crate::value::Value;
use crate::vm::{ use crate::vm::{
OP_ADD, OP_BITAND, OP_BITOR, OP_BITXOR, OP_CONSTANT, OP_DIVIDE, OP_FALSE, OP_MULTIPLY, OP_ADD, OP_BITAND, OP_BITOR, OP_BITXOR, OP_CONSTANT, OP_DIVIDE, OP_FALSE, OP_MULTIPLY,
OP_NEGATE, OP_RETURN, OP_SUBTRACT, OP_TRUE, OP_NOT, OP_SHL, OP_SHR, OP_LESS, OP_LESS_EQUAL, OP_NEGATE, OP_RETURN, OP_SUBTRACT, OP_TRUE, OP_NOT, OP_SHL, OP_SHR, OP_LESS, OP_LESS_EQUAL,
OP_GREATER, OP_GREATER_EQUAL, OP_EQUAL, OP_PRINT, OP_POP, OP_DEFINE, OP_GET OP_GREATER, OP_GREATER_EQUAL, OP_EQUAL, OP_PRINT, OP_POP, OP_DEFINE, OP_GET,OP_DEF_STRING
}; };
pub struct Chunk { pub struct Chunk {
@ -74,9 +74,10 @@ impl Chunk {
OP_PRINT => self.simple_inst("PRT", offset), OP_PRINT => self.simple_inst("PRT", offset),
OP_POP => self.simple_inst("POP", offset), OP_POP => self.simple_inst("POP", offset),
OP_DEFINE => self.constant_inst("DEF", offset), OP_DEFINE => self.constant_inst("DEF", offset),
OP_DEF_STRING => self.constant_inst("DEFSTR", offset),
OP_GET => self.constant_inst("GET", offset), OP_GET => self.constant_inst("GET", offset),
_ => { _ => {
println!("Unknown instruction"); println!("Unknown instruction {}", instruction);
offset + 1 offset + 1
} }
} }

View file

@ -3,12 +3,15 @@ use crate::scanner::scan;
use crate::tokens::{Token, TokenType}; use crate::tokens::{Token, TokenType};
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_DEFINE, OP_DIVIDE, OP_EQUAL, OP_FALSE, OP_ADD, OP_BITAND, OP_BITOR, OP_BITXOR, OP_CONSTANT, OP_DEF_BOOL, OP_DEF_CHAR, OP_DEF_DATE,
OP_GET, OP_GREATER, OP_GREATER_EQUAL, OP_LESS, OP_LESS_EQUAL, OP_MULTIPLY, OP_NEGATE, OP_NOT, OP_DEF_I32, OP_DEF_I64, OP_DEF_LIST, OP_DEF_MAP, OP_DEF_OBJ, OP_DEF_STRING, OP_DEFINE,
OP_POP, OP_PRINT, OP_RETURN, OP_SHL, OP_SHR, OP_SUBTRACT, OP_TRUE, OP_DIVIDE, OP_EQUAL, OP_FALSE, 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,
OP_TRUE,
}; };
use anyhow::anyhow; use anyhow::anyhow;
use std::collections::HashMap; use std::collections::HashMap;
use std::mem::discriminant;
use std::sync::LazyLock; use std::sync::LazyLock;
use tracing::debug; use tracing::debug;
@ -23,6 +26,8 @@ pub fn compile(source: &str) -> anyhow::Result<Chunk> {
current_token: &tokens[0], current_token: &tokens[0],
tokens: &tokens, tokens: &tokens,
current: 0, current: 0,
types: vec![],
locals: vec![],
previous: 0, previous: 0,
had_error: false, had_error: false,
}; };
@ -36,6 +41,8 @@ struct Compiler<'a> {
current: usize, current: usize,
previous_token: &'a Token, previous_token: &'a Token,
current_token: &'a Token, current_token: &'a Token,
types: Vec<Token>,
locals: Vec<String>,
previous: usize, previous: usize,
had_error: bool, had_error: bool,
} }
@ -63,10 +70,28 @@ impl<'a> Compiler<'a> {
fn let_declaration(&mut self) -> anyhow::Result<()> { fn let_declaration(&mut self) -> anyhow::Result<()> {
let index = self.parse_variable("Expect variable name")?; let index = self.parse_variable("Expect variable name")?;
let mut var_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::String
| TokenType::Char
| TokenType::Bool
| TokenType::ListType
| TokenType::MapType => var_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) { if self.match_token(TokenType::Equal) {
self.expression()?; self.expression(var_type)?;
self.consume(TokenType::Eol, "Expect end of line")?; self.consume(TokenType::Eol, "Expect end of line")?;
self.define_variable(index)?; self.define_variable(var_type, index)?;
} else { } else {
return Err(anyhow!( return Err(anyhow!(
"You cannot declare a variable without initializing it." "You cannot declare a variable without initializing it."
@ -86,8 +111,23 @@ impl<'a> Compiler<'a> {
Ok(index) Ok(index)
} }
fn define_variable(&mut self, index: usize) -> anyhow::Result<()> { fn define_variable(&mut self, var_type: Option<TokenType>, index: usize) -> anyhow::Result<()> {
self.emit_bytes(OP_DEFINE, index as u16); 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::String) => 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_OBJ,
_ => OP_DEFINE,
};
self.emit_bytes(def_op, index as u16);
Ok(()) Ok(())
} }
@ -101,14 +141,17 @@ impl<'a> Compiler<'a> {
fn expression_statement(&mut self) -> anyhow::Result<()> { fn expression_statement(&mut self) -> anyhow::Result<()> {
debug!("expression statement"); debug!("expression statement");
self.expression()?; self.expression(None)?;
self.emit_byte(OP_POP); self.emit_byte(OP_POP);
Ok(()) Ok(())
} }
fn print_statement(&mut self) -> anyhow::Result<()> { fn print_statement(&mut self) -> anyhow::Result<()> {
self.expression()?; self.expression(None)?;
self.consume(TokenType::Eol, "No further expressions expected. Please continue on a new line after the first.\n")?; self.consume(
TokenType::Eol,
"No further statements expected. Please start on a new line after the first one.\n",
)?;
self.emit_byte(OP_PRINT); self.emit_byte(OP_PRINT);
Ok(()) Ok(())
} }
@ -158,23 +201,27 @@ impl<'a> Compiler<'a> {
self.current_token.token_type == token_type self.current_token.token_type == token_type
} }
fn expression(&mut self) -> anyhow::Result<()> { fn expression(&mut self, expected_type: Option<TokenType>) -> anyhow::Result<()> {
self.parse_precedence(PREC_ASSIGNMENT)?; self.parse_precedence(PREC_ASSIGNMENT, expected_type)?;
Ok(()) Ok(())
} }
fn parse_precedence(&mut self, precedence: usize) -> anyhow::Result<()> { fn parse_precedence(
&mut self,
precedence: usize,
expected_type: Option<TokenType>,
) -> anyhow::Result<()> {
self.advance()?; self.advance()?;
let rule = get_rule(&self.previous_token.token_type); let rule = get_rule(&self.previous_token.token_type);
debug!("Precedence rule: {:?}", rule); debug!("Precedence rule: {:?}", rule);
if let Some(prefix) = rule.prefix { if let Some(prefix) = rule.prefix {
prefix(self)?; prefix(self, expected_type)?;
while precedence <= get_rule(&self.current_token.token_type).precedence { while precedence <= get_rule(&self.current_token.token_type).precedence {
self.advance()?; self.advance()?;
let infix_rule = get_rule(&self.previous_token.token_type).infix; let infix_rule = get_rule(&self.previous_token.token_type).infix;
if let Some(infix) = infix_rule { if let Some(infix) = infix_rule {
infix(self)?; infix(self, expected_type)?;
} }
} }
} else { } else {
@ -198,7 +245,7 @@ impl<'a> Compiler<'a> {
} }
} }
type ParseFn = fn(&mut Compiler) -> anyhow::Result<()>; type ParseFn = fn(&mut Compiler, expected_type: Option<TokenType>) -> anyhow::Result<()>;
#[derive(Debug)] #[derive(Debug)]
struct Rule { struct Rule {
@ -217,38 +264,66 @@ impl Rule {
} }
} }
fn number(s: &mut Compiler) -> anyhow::Result<()> { fn number(s: &mut Compiler, expected_type: Option<TokenType>) -> anyhow::Result<()> {
s.emit_constant(match s.previous_token.token_type { let number = &s.previous_token.lexeme;
TokenType::Number => Value::F64(s.previous_token.lexeme.parse()?), let value = if let Some(expected_type) = expected_type {
_ => unimplemented!(), // TODO numeric types match expected_type {
}); TokenType::I32 => Value::I32(number.parse()?),
TokenType::I64 => Value::I64(number.parse()?),
TokenType::U32 => Value::U32(number.parse()?),
TokenType::U64 => Value::U64(number.parse()?),
TokenType::F32 => Value::U32(number.parse()?),
TokenType::F64 => Value::U64(number.parse()?),
_ => {return Err(anyhow!("Invalid type: expected {} value, got {}({})", expected_type, &s.previous_token.token_type, number));}
}
} else {
if let TokenType::Number = s.previous_token.token_type {
if number.contains('.'){
Value::F64(number.parse()?)
} else {
Value::I64(number.parse()?)
}
} else {
return Err(anyhow!("I did not think this would happen"))
}
};
s.emit_constant(value);
Ok(()) Ok(())
} }
fn literal(s: &mut Compiler) -> anyhow::Result<()> { fn literal(s: &mut Compiler, expected_type: Option<TokenType>) -> anyhow::Result<()> {
if let Some(expected_type) = expected_type {
if discriminant(&expected_type) != discriminant(&s.previous_token.token_type) {
return Err(anyhow!(
"Cannot assign {:?} to {:?}",
s.previous_token.token_type,
expected_type
));
}
}
match s.previous_token.token_type { match s.previous_token.token_type {
TokenType::False => s.emit_constant(Value::Bool(false)), TokenType::False => s.emit_constant(Value::Bool(false)),
TokenType::True => s.emit_constant(Value::Bool(true)), TokenType::True => s.emit_constant(Value::Bool(true)),
TokenType::String => s.emit_constant(Value::String(s.previous_token.lexeme.clone())), TokenType::Text => s.emit_constant(Value::String(s.previous_token.lexeme.clone())),
_ => {} _ => {}
} }
Ok(()) Ok(())
} }
fn skip(s: &mut Compiler) -> anyhow::Result<()> { fn skip(s: &mut Compiler, expected_type: Option<TokenType>) -> anyhow::Result<()> {
// s.advance()
Ok(()) Ok(())
} }
fn grouping(s: &mut Compiler) -> anyhow::Result<()> { fn grouping(s: &mut Compiler, expected_type: Option<TokenType>) -> anyhow::Result<()> {
s.expression()?; s.expression(None)?;
s.consume(TokenType::RightParen, "Expect ')' after expression.") s.consume(TokenType::RightParen, "Expect ')' after expression.")
} }
fn unary(s: &mut Compiler) -> anyhow::Result<()> { fn unary(s: &mut Compiler, expected_type: Option<TokenType>) -> anyhow::Result<()> {
let operator_type = s.previous_token.token_type; let operator_type = s.previous_token.token_type;
s.parse_precedence(PREC_UNARY)?; s.parse_precedence(PREC_UNARY, None)?;
match operator_type { match operator_type {
TokenType::Minus => { TokenType::Minus => {
@ -262,11 +337,11 @@ fn unary(s: &mut Compiler) -> anyhow::Result<()> {
Ok(()) Ok(())
} }
fn binary(s: &mut Compiler) -> anyhow::Result<()> { fn binary(s: &mut Compiler, expected_type: Option<TokenType>) -> anyhow::Result<()> {
let operator_type = &s.previous_token.token_type; let operator_type = &s.previous_token.token_type;
debug!("operator {:?}", operator_type); debug!("operator {:?}", operator_type);
let rule = get_rule(operator_type); let rule = get_rule(operator_type);
s.parse_precedence(rule.precedence + 1)?; s.parse_precedence(rule.precedence + 1, None)?;
match operator_type { match operator_type {
TokenType::Plus => s.emit_byte(OP_ADD), TokenType::Plus => s.emit_byte(OP_ADD),
TokenType::Minus => s.emit_byte(OP_SUBTRACT), TokenType::Minus => s.emit_byte(OP_SUBTRACT),
@ -287,7 +362,7 @@ fn binary(s: &mut Compiler) -> anyhow::Result<()> {
Ok(()) Ok(())
} }
fn variable(s: &mut Compiler) -> anyhow::Result<()> { fn variable(s: &mut Compiler, expected_type: Option<TokenType>) -> anyhow::Result<()> {
let index = s.identifier_constant(s.previous_token)?; let index = s.identifier_constant(s.previous_token)?;
s.emit_bytes(OP_GET, index as u16); s.emit_bytes(OP_GET, index as u16);
Ok(()) Ok(())
@ -309,7 +384,7 @@ static RULES: LazyLock<HashMap<TokenType, Rule>> = LazyLock::new(|| {
); );
rules.insert(TokenType::Colon, Rule::new(None, None, PREC_NONE)); rules.insert(TokenType::Colon, Rule::new(None, None, PREC_NONE));
rules.insert(TokenType::Comma, Rule::new(None, None, PREC_NONE)); rules.insert(TokenType::Comma, Rule::new(None, None, PREC_NONE));
rules.insert(TokenType::DateType, 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::Dot, Rule::new(None, None, PREC_NONE));
rules.insert(TokenType::Else, 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::Eof, Rule::new(Some(skip), None, PREC_NONE));
@ -334,8 +409,8 @@ static RULES: LazyLock<HashMap<TokenType, Rule>> = LazyLock::new(|| {
TokenType::GreaterGreater, TokenType::GreaterGreater,
Rule::new(None, Some(binary), PREC_BITSHIFT), Rule::new(None, Some(binary), PREC_BITSHIFT),
); );
rules.insert(TokenType::I32Type, Rule::new(None, None, PREC_NONE)); rules.insert(TokenType::I32, Rule::new(None, None, PREC_NONE));
rules.insert(TokenType::I64Type, Rule::new(None, None, PREC_NONE)); rules.insert(TokenType::I64, Rule::new(None, None, PREC_NONE));
rules.insert( rules.insert(
TokenType::Identifier, TokenType::Identifier,
Rule::new(Some(variable), None, PREC_NONE), Rule::new(Some(variable), None, PREC_NONE),
@ -377,16 +452,16 @@ static RULES: LazyLock<HashMap<TokenType, Rule>> = LazyLock::new(|| {
rules.insert(TokenType::RightBracket, 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::Slash, Rule::new(None, Some(binary), PREC_FACTOR));
rules.insert(TokenType::Star, Rule::new(None, Some(binary), PREC_FACTOR)); rules.insert(TokenType::Star, Rule::new(None, Some(binary), PREC_FACTOR));
rules.insert(TokenType::String, Rule::new(Some(literal), None, PREC_NONE)); rules.insert(TokenType::Text, Rule::new(Some(literal), None, PREC_NONE));
rules.insert( rules.insert(
TokenType::BitAnd, TokenType::BitAnd,
Rule::new(None, Some(binary), PREC_BITAND), Rule::new(None, Some(binary), PREC_BITAND),
); );
rules.insert(TokenType::StringType, Rule::new(None, None, PREC_NONE)); rules.insert(TokenType::String, Rule::new(None, None, PREC_NONE));
rules.insert(TokenType::Struct, 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::True, Rule::new(Some(literal), None, PREC_NONE));
rules.insert(TokenType::U32Type, Rule::new(None, None, PREC_NONE)); rules.insert(TokenType::U32, Rule::new(None, None, PREC_NONE));
rules.insert(TokenType::U64Type, 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.insert(TokenType::While, Rule::new(None, None, PREC_NONE));
rules rules
@ -407,3 +482,19 @@ const PREC_FACTOR: usize = 11;
const PREC_UNARY: usize = 12; const PREC_UNARY: usize = 12;
const PREC_CALL: usize = 13; const PREC_CALL: usize = 13;
const PREC_PRIMARY: usize = 14; const PREC_PRIMARY: usize = 14;
enum ValueType{
DateType,
BoolType,
CharType,
F32Type,
F64Type,
I32Type,
I64Type,
ObjectType,
U32Type,
U64Type,
StringType,
ListType,
MapType,
}

View file

@ -3,27 +3,29 @@ use crate::tokens::TokenType;
pub(crate) fn get_keyword(lexeme: &str) -> Option<TokenType> { pub(crate) fn get_keyword(lexeme: &str) -> Option<TokenType> {
match lexeme { match lexeme {
"and" => Some(TokenType::LogicalAnd), "and" => Some(TokenType::LogicalAnd),
"bool" => Some(TokenType::BoolType), "bool" => Some(TokenType::Bool),
"char" => Some(TokenType::CharType), "char" => Some(TokenType::Char),
"date" => Some(TokenType::DateType), "date" => Some(TokenType::Date),
"else" => Some(TokenType::Else), "else" => Some(TokenType::Else),
"false" => Some(TokenType::False), "false" => Some(TokenType::False),
"f32" => Some(TokenType::F32),
"f64" => Some(TokenType::F64),
"fn" => Some(TokenType::Fn), "fn" => Some(TokenType::Fn),
"for" => Some(TokenType::For), "for" => Some(TokenType::For),
"if" => Some(TokenType::If), "if" => Some(TokenType::If),
"i32" => Some(TokenType::I32Type), "i32" => Some(TokenType::I32),
"i64" => Some(TokenType::I64Type), "i64" => Some(TokenType::I64),
"let" => Some(TokenType::Let), "let" => Some(TokenType::Let),
"list" => Some(TokenType::ListType), "list" => Some(TokenType::ListType),
"map" => Some(TokenType::MapType), "map" => Some(TokenType::MapType),
"or" => Some(TokenType::LogicalOr), "or" => Some(TokenType::LogicalOr),
"object" => Some(TokenType::ObjectType), "object" => Some(TokenType::Object),
"print" => Some(TokenType::Print), "print" => Some(TokenType::Print),
"struct" => Some(TokenType::Struct), "struct" => Some(TokenType::Struct),
"string" => Some(TokenType::StringType), "string" => Some(TokenType::String),
"true" => Some(TokenType::True), "true" => Some(TokenType::True),
"u32" => Some(TokenType::U32Type), "u32" => Some(TokenType::U32),
"u64" => Some(TokenType::U64Type), "u64" => Some(TokenType::U64),
"while" => Some(TokenType::While), "while" => Some(TokenType::While),
_ => None, _ => None,

View file

@ -2,9 +2,7 @@ fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt::init(); tracing_subscriber::fmt::init();
let chunk = crudlang::compiler::compile( let chunk = crudlang::compiler::compile(
r#"let a = "hello " + 42 r#"let a:bool = 42"#,
print a print a
print a"#,
); );
match chunk { match chunk {
Err(e) => { Err(e) => {

View file

@ -179,7 +179,7 @@ impl Scanner {
let value: String = self.chars[self.start + 1..self.current - 1] let value: String = self.chars[self.start + 1..self.current - 1]
.iter() .iter()
.collect(); .collect();
self.add_token_with_value(TokenType::String, value); self.add_token_with_value(TokenType::Text, value);
} }
fn peek(&self) -> char { fn peek(&self) -> char {

View file

@ -1,3 +1,5 @@
use std::fmt;
#[derive(Debug)] #[derive(Debug)]
pub struct Token { pub struct Token {
pub token_type: TokenType, pub token_type: TokenType,
@ -27,11 +29,11 @@ pub(crate) enum TokenType {
BitAnd, BitAnd,
BitOr, BitOr,
BitXor, BitXor,
BoolType, Bool,
CharType, Char,
Colon, Colon,
Comma, Comma,
DateType, Date,
Dot, Dot,
Else, Else,
Eof, Eof,
@ -39,6 +41,8 @@ pub(crate) enum TokenType {
Equal, Equal,
EqualEqual, EqualEqual,
Error, Error,
F32,
F64,
False, False,
Fn, Fn,
For, For,
@ -46,8 +50,8 @@ pub(crate) enum TokenType {
GreaterEqual, GreaterEqual,
GreaterGreater, GreaterGreater,
Hash, Hash,
I32Type, I32,
I64Type, I64,
If, If,
Indent, Indent,
Identifier, Identifier,
@ -65,7 +69,7 @@ pub(crate) enum TokenType {
Minus, Minus,
Not, Not,
Number, Number,
ObjectType, Object,
Plus, Plus,
Print, Print,
Return, Return,
@ -75,15 +79,84 @@ pub(crate) enum TokenType {
Semicolon, Semicolon,
Slash, Slash,
Star, Star,
Text,
String, String,
StringType,
Struct, Struct,
True, True,
U32Type, U32,
U64Type, U64,
While, While,
} }
impl fmt::Display for TokenType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TokenType::String => write!(f, "string"),
TokenType::Date => write!(f, "date"),
TokenType::Char => write!(f, "char"),
TokenType::I32 => write!(f, "i32"),
TokenType::I64 => write!(f, "i64"),
TokenType::U32 => write!(f, "u32"),
TokenType::U64 => write!(f, "u64"),
TokenType::F32 => write!(f, "f32"),
TokenType::F64 => write!(f, "f64"),
TokenType::Bool => write!(f, "bool"),
TokenType::Bang=> write!(f, "!"),
TokenType::BangEqual=> write!(f, "!="),
TokenType::BitAnd=> write!(f, "&"),
TokenType::BitOr=> write!(f, "|"),
TokenType::BitXor=> write!(f, "^"),
TokenType::Colon=> write!(f, ":"),
TokenType::Comma=> write!(f, ","),
TokenType::MapType => write!(f, "map"),
TokenType::ListType => write!(f, "list"),
TokenType::Dot => write!(f, "."),
TokenType::Else => write!(f, "else"),
TokenType::Eof => write!(f, "EOF"),
TokenType::Eol => write!(f, "EOL"),
TokenType::Equal => write!(f, "="),
TokenType::EqualEqual => write!(f, "=="),
TokenType::Error => write!(f, "error"),
TokenType::False => write!(f, "false"),
TokenType::Fn => write!(f, "fn"),
TokenType::For => write!(f, "for"),
TokenType::Greater => write!(f, ">"),
TokenType::GreaterEqual => write!(f, ">="),
TokenType::GreaterGreater => write!(f, ">>"),
TokenType::Hash => write!(f, "#"),
TokenType::If => write!(f, "if"),
TokenType::Indent => write!(f, "indent"),
TokenType::Identifier => write!(f, "identifier"),
TokenType::LeftBrace => write!(f, "{{"),
TokenType::LeftBracket => write!(f, "["),
TokenType::LeftParen => write!(f, "("),
TokenType::Less => write!(f, "<"),
TokenType::LessEqual => write!(f, "<="),
TokenType::LessLess => write!(f, "<<"),
TokenType::Let => write!(f, "let"),
TokenType::LogicalAnd => write!(f, "&&"),
TokenType::LogicalOr => write!(f, "||"),
TokenType::Minus => write!(f, "-"),
TokenType::Not => write!(f, "not"),
TokenType::Number => write!(f, "number"),
TokenType::Object => write!(f, "object"),
TokenType::Plus => write!(f, "+"),
TokenType::Print => write!(f, "print"),
TokenType::Return => write!(f, "return"),
TokenType::RightParen => write!(f, ")"),
TokenType::RightBrace => write!(f, "}}"),
TokenType::RightBracket => write!(f, "]"),
TokenType::Semicolon => write!(f, ";"),
TokenType::Slash => write!(f, "/"),
TokenType::Star => write!(f, "*"),
TokenType::Text => write!(f, "text"),
TokenType::Struct => write!(f, "struct"),
TokenType::True => write!(f, "true"),
TokenType::While => write!(f, "while"),
}
}
}
impl Eq for TokenType { impl Eq for TokenType {
} }

View file

@ -81,6 +81,13 @@ impl Vm {
let value = self.pop(); let value = self.pop();
self.local_vars.insert(name, value); self.local_vars.insert(name, value);
} }
OP_DEF_I32 => {
let name = self.read_constant();
let value = self.pop();
if let Value::I32(v) = value {
self.local_vars.insert(name, value);
}
}
OP_GET => { OP_GET => {
let name = self.read_constant(); let name = self.read_constant();
let value = self.local_vars.get(&name).unwrap(); let value = self.local_vars.get(&name).unwrap();
@ -160,3 +167,13 @@ pub const OP_SHL: u16 = 24;
pub const OP_POP: u16 = 25; pub const OP_POP: u16 = 25;
pub const OP_DEFINE: u16 = 26; pub const OP_DEFINE: u16 = 26;
pub const OP_GET: u16 = 27; pub const OP_GET: u16 = 27;
pub const OP_DEF_I32: u16 = 28;
pub const OP_DEF_I64: u16 = 29;
pub const OP_DEF_U32: u16 = 30;
pub const OP_DEF_DATE: u16 = 31;
pub const OP_DEF_STRING: u16 = 32;
pub const OP_DEF_CHAR: u16 = 33;
pub const OP_DEF_BOOL: u16 = 34;
pub const OP_DEF_LIST: u16 = 35;
pub const OP_DEF_MAP: u16 = 36;
pub const OP_DEF_OBJ: u16 = 37;