function calls, with arguments
This commit is contained in:
parent
50de71572f
commit
84835da814
9 changed files with 365 additions and 111 deletions
|
|
@ -1,17 +1,20 @@
|
||||||
use crate::ast_compiler::Expression::Variable;
|
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, Greater,
|
|
||||||
GreaterEqual, GreaterGreater, I32, I64, Identifier, Integer, LeftParen, Less, LessEqual,
|
|
||||||
LessLess, Let, ListType, MapType, Minus, Object, Plus, Print, RightParen, Slash, Star, Text,
|
|
||||||
True, U32, U64,
|
|
||||||
};
|
|
||||||
use crate::tokens::{Token, TokenType};
|
use crate::tokens::{Token, TokenType};
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
pub fn compile(tokens: Vec<Token>) -> anyhow::Result<Vec<Statement>> {
|
pub fn compile(tokens: Vec<Token>) -> anyhow::Result<Vec<Statement>> {
|
||||||
let mut compiler = AstCompiler::new(tokens);
|
let mut compiler = AstCompiler::new(tokens);
|
||||||
compiler.compile()
|
compiler.compile(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub(crate) struct Function {
|
||||||
|
pub(crate) name: Token,
|
||||||
|
pub(crate) parameters: Vec<Parameter>,
|
||||||
|
pub(crate) return_type: TokenType,
|
||||||
|
pub(crate) body: Vec<Statement>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AstCompiler {
|
struct AstCompiler {
|
||||||
|
|
@ -19,6 +22,8 @@ struct AstCompiler {
|
||||||
current: usize,
|
current: usize,
|
||||||
had_error: bool,
|
had_error: bool,
|
||||||
vars: Vec<Expression>,
|
vars: Vec<Expression>,
|
||||||
|
indent: Vec<usize>,
|
||||||
|
functions: HashMap<String, Function>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AstCompiler {
|
impl AstCompiler {
|
||||||
|
|
@ -28,25 +33,109 @@ impl AstCompiler {
|
||||||
current: 0,
|
current: 0,
|
||||||
had_error: false,
|
had_error: false,
|
||||||
vars: vec![],
|
vars: vec![],
|
||||||
|
indent: vec![],
|
||||||
|
functions: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile(&mut self) -> anyhow::Result<Vec<Statement>> {
|
fn compile(&mut self, expected_indent: usize) -> anyhow::Result<Vec<Statement>> {
|
||||||
let mut statements = vec![];
|
let mut statements = vec![];
|
||||||
while !self.is_at_end() {
|
while !self.is_at_end() {
|
||||||
statements.push(self.declaration()?)
|
let statement = self.indent(expected_indent)?;
|
||||||
|
if let Some(statement) = statement {
|
||||||
|
statements.push(statement);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(statements)
|
Ok(statements)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn indent(&mut self, expected_indent: usize) -> anyhow::Result<Option<Statement>> {
|
||||||
|
// skip empty lines
|
||||||
|
while self.check(Eol) {
|
||||||
|
self.advance();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut indent_on_line = 0;
|
||||||
|
// keep track of indent level
|
||||||
|
while self.match_token(vec![Indent]) {
|
||||||
|
indent_on_line += 1;
|
||||||
|
}
|
||||||
|
if indent_on_line > expected_indent {
|
||||||
|
panic!(
|
||||||
|
"unexpected indent level {} vs {}",
|
||||||
|
indent_on_line, expected_indent
|
||||||
|
);
|
||||||
|
} else if indent_on_line < expected_indent {
|
||||||
|
self.indent.pop();
|
||||||
|
return Ok(None);
|
||||||
|
} else {
|
||||||
|
self.indent.push(indent_on_line);
|
||||||
|
Ok(Some(self.declaration()?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn declaration(&mut self) -> anyhow::Result<Statement> {
|
fn declaration(&mut self) -> anyhow::Result<Statement> {
|
||||||
if self.match_token(vec![Let]) {
|
if self.match_token(vec![Fn]) {
|
||||||
|
self.function_declaration()
|
||||||
|
} else if self.match_token(vec![Let]) {
|
||||||
self.let_declaration()
|
self.let_declaration()
|
||||||
} else {
|
} else {
|
||||||
self.statement()
|
self.statement()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn function_declaration(&mut self) -> anyhow::Result<Statement> {
|
||||||
|
let name_token = self.consume(Identifier, "Expect function name.")?;
|
||||||
|
self.consume(LeftParen, "Expect '(' after function name.")?;
|
||||||
|
let mut parameters = vec![];
|
||||||
|
while !self.check(RightParen) {
|
||||||
|
if parameters.len() >= 25 {
|
||||||
|
return Err(anyhow::anyhow!("Too many parameters."));
|
||||||
|
}
|
||||||
|
let parm_name = self.consume(Identifier, "Expect parameter name.")?;
|
||||||
|
|
||||||
|
self.consume(Colon, "Expect : after parameter name")?;
|
||||||
|
let var_type = self.peek().token_type;
|
||||||
|
self.vars.push(Expression::Variable {
|
||||||
|
name: parm_name.lexeme.to_string(),
|
||||||
|
var_type,
|
||||||
|
line: parm_name.line,
|
||||||
|
});
|
||||||
|
self.advance();
|
||||||
|
parameters.push(Parameter {
|
||||||
|
name: parm_name,
|
||||||
|
var_type,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
self.consume(RightParen, "Expect ')' after parameters.")?;
|
||||||
|
let return_type = if self.check(SingleRightArrow) {
|
||||||
|
self.consume(SingleRightArrow, "")?;
|
||||||
|
self.advance().token_type
|
||||||
|
} else {
|
||||||
|
TokenType::Void
|
||||||
|
};
|
||||||
|
self.consume(Colon, "Expect colon (:) after function declaration.")?;
|
||||||
|
self.consume(Eol, "Expect end of line.")?;
|
||||||
|
|
||||||
|
let current_indent = self.indent.last().unwrap();
|
||||||
|
let body = self.compile(current_indent + 1)?;
|
||||||
|
|
||||||
|
let function = Function {
|
||||||
|
name: name_token.clone(),
|
||||||
|
parameters,
|
||||||
|
return_type,
|
||||||
|
body,
|
||||||
|
};
|
||||||
|
|
||||||
|
let function_stmt = Statement::FunctionStmt {
|
||||||
|
function: function.clone(),
|
||||||
|
};
|
||||||
|
self.functions.insert(name_token.lexeme, function.clone());
|
||||||
|
Ok(function_stmt)
|
||||||
|
}
|
||||||
|
|
||||||
fn let_declaration(&mut self) -> anyhow::Result<Statement> {
|
fn let_declaration(&mut self) -> anyhow::Result<Statement> {
|
||||||
let name_token = self.consume(Identifier, "Expect variable name.")?;
|
let name_token = self.consume(Identifier, "Expect variable name.")?;
|
||||||
|
|
||||||
|
|
@ -70,7 +159,7 @@ impl AstCompiler {
|
||||||
return Err(e);
|
return Err(e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.vars.push(Variable {
|
self.vars.push(Expression::Variable {
|
||||||
name: name_token.lexeme.to_string(),
|
name: name_token.lexeme.to_string(),
|
||||||
var_type,
|
var_type,
|
||||||
line: name_token.line,
|
line: name_token.line,
|
||||||
|
|
@ -95,8 +184,8 @@ impl AstCompiler {
|
||||||
|
|
||||||
fn print_statement(&mut self) -> anyhow::Result<Statement> {
|
fn print_statement(&mut self) -> anyhow::Result<Statement> {
|
||||||
let expr = self.expression()?;
|
let expr = self.expression()?;
|
||||||
self.consume(Eol, "Expect end of line after expression.")?;
|
self.consume(Eol, "Expect end of line after print statement.")?;
|
||||||
Ok(Statement::Print { value: expr })
|
Ok(Statement::PrintStmt { value: expr })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expr_statement(&mut self) -> anyhow::Result<Statement> {
|
fn expr_statement(&mut self) -> anyhow::Result<Statement> {
|
||||||
|
|
@ -192,6 +281,7 @@ impl AstCompiler {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn primary(&mut self) -> anyhow::Result<Expression> {
|
fn primary(&mut self) -> anyhow::Result<Expression> {
|
||||||
|
debug!("primary {:?}", self.peek());
|
||||||
Ok(if self.match_token(vec![False]) {
|
Ok(if self.match_token(vec![False]) {
|
||||||
Expression::Literal {
|
Expression::Literal {
|
||||||
line: self.peek().line,
|
line: self.peek().line,
|
||||||
|
|
@ -216,10 +306,10 @@ impl AstCompiler {
|
||||||
literaltype: FloatingPoint,
|
literaltype: FloatingPoint,
|
||||||
value: Value::F64(self.previous().lexeme.parse()?),
|
value: Value::F64(self.previous().lexeme.parse()?),
|
||||||
}
|
}
|
||||||
} else if self.match_token(vec![Text]) {
|
} else if self.match_token(vec![StringType]) {
|
||||||
Expression::Literal {
|
Expression::Literal {
|
||||||
line: self.peek().line,
|
line: self.peek().line,
|
||||||
literaltype: Text,
|
literaltype: StringType,
|
||||||
value: Value::String(self.previous().lexeme.to_string()),
|
value: Value::String(self.previous().lexeme.to_string()),
|
||||||
}
|
}
|
||||||
} else if self.match_token(vec![LeftParen]) {
|
} else if self.match_token(vec![LeftParen]) {
|
||||||
|
|
@ -231,23 +321,67 @@ impl AstCompiler {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let token = self.advance().clone();
|
let token = self.advance().clone();
|
||||||
|
debug!("{:?}", token);
|
||||||
|
if self.match_token(vec![LeftParen]) {
|
||||||
|
self.function_call(token.lexeme)?
|
||||||
|
} else {
|
||||||
|
self.variable_lookup(&token)?
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn variable_lookup(&mut self, token: &Token) -> anyhow::Result<Expression> {
|
||||||
let (var_name, var_type) = self
|
let (var_name, var_type) = self
|
||||||
.vars
|
.vars
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|e| {
|
.filter_map(|e| {
|
||||||
if let Variable { name, var_type, .. } = e {
|
if let Expression::Variable { name, var_type, .. } = e {
|
||||||
Some((name, var_type))
|
Some((name, var_type))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.find(|e| e.0 == &token.lexeme)
|
.find(|e| e.0 == &token.lexeme)
|
||||||
.ok_or_else(|| return anyhow::anyhow!("Unknown variable: {}", token.lexeme))?;
|
.ok_or_else(|| return anyhow::anyhow!("Unknown variable: {:?}", token))?;
|
||||||
Variable {
|
Ok(Expression::Variable {
|
||||||
name: var_name.to_string(),
|
name: var_name.to_string(),
|
||||||
var_type: var_type.clone(),
|
var_type: var_type.clone(),
|
||||||
line: token.line,
|
line: token.line,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn function_call(&mut self, name: String) -> anyhow::Result<Expression> {
|
||||||
|
let function_name = self.functions.get(&name).unwrap().name.lexeme.clone();
|
||||||
|
let function = self.functions.get(&function_name).unwrap().clone();
|
||||||
|
|
||||||
|
let mut arguments = vec![];
|
||||||
|
while !self.match_token(vec![RightParen]) {
|
||||||
|
if arguments.len() >= 25 {
|
||||||
|
return Err(anyhow::anyhow!("Too many parameters."));
|
||||||
|
}
|
||||||
|
let arg = self.expression()?;
|
||||||
|
let arg_type = arg.infer_type();
|
||||||
|
if arg_type != function.parameters[arguments.len()].var_type {
|
||||||
|
return Err(anyhow::anyhow!(
|
||||||
|
"Incompatible argument types. Expected {}, found {}",
|
||||||
|
function.parameters[arguments.len()].var_type,
|
||||||
|
arg_type
|
||||||
|
));
|
||||||
|
}
|
||||||
|
arguments.push(arg);
|
||||||
|
if self.peek().token_type == TokenType::Comma {
|
||||||
|
self.advance();
|
||||||
|
} else {
|
||||||
|
self.consume(RightParen, "Expect ')' after arguments.")?;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let return_type = self.functions.get(&name).unwrap().return_type;
|
||||||
|
Ok(Expression::FunctionCall {
|
||||||
|
line: self.peek().line,
|
||||||
|
name,
|
||||||
|
arguments,
|
||||||
|
return_type,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -303,7 +437,9 @@ 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,
|
||||||
|
|
@ -312,6 +448,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,
|
||||||
_ => {
|
_ => {
|
||||||
return Err(anyhow::anyhow!(
|
return Err(anyhow::anyhow!(
|
||||||
"Incompatible types. Expected {}, found {}",
|
"Incompatible types. Expected {}, found {}",
|
||||||
|
|
@ -338,7 +475,7 @@ fn calculate_type(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Statement {
|
pub enum Statement {
|
||||||
ExpressionStmt {
|
ExpressionStmt {
|
||||||
expression: Expression,
|
expression: Expression,
|
||||||
|
|
@ -348,26 +485,32 @@ pub enum Statement {
|
||||||
var_type: TokenType,
|
var_type: TokenType,
|
||||||
initializer: Expression,
|
initializer: Expression,
|
||||||
},
|
},
|
||||||
Print {
|
PrintStmt {
|
||||||
value: Expression,
|
value: Expression,
|
||||||
},
|
},
|
||||||
|
FunctionStmt {
|
||||||
|
function: Function,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Statement {
|
impl Statement {
|
||||||
pub fn line(&self) -> usize {
|
pub fn line(&self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
Statement::ExpressionStmt { expression } => expression.line(),
|
Statement::ExpressionStmt { expression } => expression.line(),
|
||||||
Statement::VarStmt {
|
Statement::VarStmt { name, .. } => name.line,
|
||||||
name,
|
Statement::PrintStmt { value } => value.line(),
|
||||||
var_type,
|
Statement::FunctionStmt { function, .. } => function.name.line,
|
||||||
initializer,
|
|
||||||
} => name.line,
|
|
||||||
Statement::Print { value } => value.line(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Parameter {
|
||||||
|
pub(crate) name: Token,
|
||||||
|
pub(crate) var_type: TokenType,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
pub enum Expression {
|
pub enum Expression {
|
||||||
Binary {
|
Binary {
|
||||||
line: usize,
|
line: usize,
|
||||||
|
|
@ -394,6 +537,12 @@ pub enum Expression {
|
||||||
name: String,
|
name: String,
|
||||||
var_type: TokenType,
|
var_type: TokenType,
|
||||||
},
|
},
|
||||||
|
FunctionCall {
|
||||||
|
line: usize,
|
||||||
|
name: String,
|
||||||
|
arguments: Vec<Expression>,
|
||||||
|
return_type: TokenType,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Expression {
|
impl Expression {
|
||||||
|
|
@ -404,6 +553,7 @@ impl Expression {
|
||||||
Self::Grouping { line, expression } => *line,
|
Self::Grouping { line, expression } => *line,
|
||||||
Self::Literal { line, .. } => *line,
|
Self::Literal { line, .. } => *line,
|
||||||
Self::Variable { line, .. } => *line,
|
Self::Variable { line, .. } => *line,
|
||||||
|
Self::FunctionCall { line, .. } => *line,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -432,8 +582,8 @@ impl Expression {
|
||||||
// followed by type coercion to 64 bits for numeric types
|
// followed by type coercion to 64 bits for numeric types
|
||||||
debug!("coerce {} : {}", left_type, right_type);
|
debug!("coerce {} : {}", left_type, right_type);
|
||||||
match (left_type, right_type) {
|
match (left_type, right_type) {
|
||||||
(_, Text) => Text,
|
(_, StringType) => StringType,
|
||||||
(Text, _) => Text,
|
(StringType, _) => StringType,
|
||||||
(FloatingPoint, _) => F64,
|
(FloatingPoint, _) => F64,
|
||||||
(Integer, FloatingPoint) => F64,
|
(Integer, FloatingPoint) => F64,
|
||||||
(Integer, _) => I64,
|
(Integer, _) => I64,
|
||||||
|
|
@ -469,6 +619,7 @@ impl Expression {
|
||||||
Self::Literal { literaltype, .. } => literaltype.clone(),
|
Self::Literal { 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(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,34 @@
|
||||||
use std::collections::HashMap;
|
use crate::ast_compiler::{Expression, Function, Parameter, Statement};
|
||||||
use crate::ast_compiler::{Expression, 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::{OP_ADD, OP_BITAND, OP_BITOR, OP_BITXOR, OP_CONSTANT, OP_DEF_BOOL, OP_DEF_CHAR, 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_GREATER_EQUAL, OP_LESS, OP_LESS_EQUAL, OP_MULTIPLY, OP_NEGATE, OP_NOT, OP_PRINT, OP_RETURN, OP_SHL, OP_SHR, OP_SUBTRACT, OP_AND, OP_OR};
|
use crate::vm::{
|
||||||
|
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_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_RETURN, OP_SHL, OP_SHR, OP_SUBTRACT,
|
||||||
|
};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
pub fn compile(ast: Vec<Statement>) -> anyhow::Result<Chunk> {
|
pub fn compile(ast: &Vec<Statement>) -> anyhow::Result<Chunk> {
|
||||||
let compiler = Compiler::new();
|
compile_name(ast, "_")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn compile_function(function: &Function) -> anyhow::Result<Chunk> {
|
||||||
|
let mut compiler = Compiler::new(&function.name.lexeme);
|
||||||
|
for parm in &function.parameters{
|
||||||
|
let name = parm.name.lexeme.clone();
|
||||||
|
let name_index = compiler.chunk.add_constant(Value::String(name.clone()));
|
||||||
|
compiler.vars.insert(name, name_index);
|
||||||
|
compiler.emit_bytes(OP_DEFINE, name_index as u16);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(compiler.compile(&function.body)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn compile_name(ast: &Vec<Statement>, name: &str) -> anyhow::Result<Chunk> {
|
||||||
|
let compiler = Compiler::new(name);
|
||||||
Ok(compiler.compile(ast)?)
|
Ok(compiler.compile(ast)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -15,20 +37,22 @@ struct Compiler {
|
||||||
had_error: bool,
|
had_error: bool,
|
||||||
current_line: usize,
|
current_line: usize,
|
||||||
vars: HashMap<String, usize>,
|
vars: HashMap<String, usize>,
|
||||||
|
functions: HashMap<String, usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Compiler {
|
impl Compiler {
|
||||||
fn new() -> Self {
|
fn new(name: &str) -> Self {
|
||||||
Self {
|
Self {
|
||||||
chunk: Chunk::new("main"),
|
chunk: Chunk::new(name),
|
||||||
had_error: false,
|
had_error: false,
|
||||||
current_line: 0,
|
current_line: 0,
|
||||||
vars: HashMap::new(),
|
vars: HashMap::new(),
|
||||||
|
functions: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile(mut self, ast: Vec<Statement>) -> anyhow::Result<Chunk> {
|
fn compile(mut self, ast: &Vec<Statement>) -> anyhow::Result<Chunk> {
|
||||||
for statement in &ast {
|
for statement in ast {
|
||||||
self.compile_statement(statement)?
|
self.compile_statement(statement)?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -45,22 +69,41 @@ impl Compiler {
|
||||||
initializer,
|
initializer,
|
||||||
} => {
|
} => {
|
||||||
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)?
|
||||||
}
|
}
|
||||||
Statement::Print { value } => {
|
Statement::PrintStmt { value } => {
|
||||||
self.compile_expression(value)?;
|
self.compile_expression(value)?;
|
||||||
self.emit_byte(OP_PRINT);
|
self.emit_byte(OP_PRINT);
|
||||||
}
|
}
|
||||||
_ => unimplemented!(),
|
Statement::ExpressionStmt { expression } => {
|
||||||
|
self.compile_expression(expression)?;
|
||||||
|
}
|
||||||
|
Statement::FunctionStmt { function } => {
|
||||||
|
let function_name = function.name.lexeme.clone();
|
||||||
|
let compiled_function = compile_function(function)?;
|
||||||
|
let name_index = self.chunk.add_function(compiled_function);
|
||||||
|
self.functions
|
||||||
|
.insert(function_name, name_index);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_expression(&mut self, expression: &Expression) -> anyhow::Result<()> {
|
fn compile_expression(&mut self, expression: &Expression) -> anyhow::Result<()> {
|
||||||
match expression {
|
match expression {
|
||||||
Expression::Variable {name, ..} => {
|
Expression::FunctionCall {
|
||||||
|
name, arguments, ..
|
||||||
|
} => {
|
||||||
|
let function_index = *self.functions.get(name).unwrap();
|
||||||
|
for argument in arguments {
|
||||||
|
self.compile_expression(argument)?;
|
||||||
|
}
|
||||||
|
self.emit_bytes(OP_CALL, function_index as u16);
|
||||||
|
self.emit_byte(arguments.len() as u16);
|
||||||
|
}
|
||||||
|
Expression::Variable { name, .. } => {
|
||||||
let name_index = self.vars.get(name).unwrap();
|
let name_index = self.vars.get(name).unwrap();
|
||||||
self.emit_bytes(OP_GET, *name_index as u16);
|
self.emit_bytes(OP_GET, *name_index as u16);
|
||||||
}
|
}
|
||||||
|
|
@ -121,7 +164,7 @@ impl Compiler {
|
||||||
TokenType::F32 => OP_DEF_F32,
|
TokenType::F32 => OP_DEF_F32,
|
||||||
TokenType::F64 => OP_DEF_F64,
|
TokenType::F64 => OP_DEF_F64,
|
||||||
TokenType::Date => OP_DEF_DATE,
|
TokenType::Date => OP_DEF_DATE,
|
||||||
TokenType::String => OP_DEF_STRING,
|
TokenType::StringType => OP_DEF_STRING,
|
||||||
TokenType::Char => OP_DEF_CHAR,
|
TokenType::Char => OP_DEF_CHAR,
|
||||||
TokenType::Bool => OP_DEF_BOOL,
|
TokenType::Bool => OP_DEF_BOOL,
|
||||||
TokenType::ListType => OP_DEF_LIST,
|
TokenType::ListType => OP_DEF_LIST,
|
||||||
|
|
|
||||||
23
src/chunk.rs
23
src/chunk.rs
|
|
@ -1,10 +1,10 @@
|
||||||
use tracing::debug;
|
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_CONSTANT, OP_DIVIDE, OP_MULTIPLY,
|
||||||
OP_NEGATE, OP_RETURN, OP_SUBTRACT, OP_NOT, OP_SHL, OP_SHR, OP_LESS, OP_LESS_EQUAL,
|
OP_NEGATE, OP_RETURN, OP_SUBTRACT, 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_DEF_STRING,
|
OP_GREATER, OP_GREATER_EQUAL, OP_EQUAL, OP_PRINT, OP_POP, OP_DEFINE, OP_GET,OP_DEF_STRING,
|
||||||
OP_DEF_I32, OP_DEF_I64, OP_DEF_F32, OP_DEF_F64, OP_DEF_BOOL
|
OP_DEF_I32, OP_DEF_I64, OP_DEF_F32, OP_DEF_F64, OP_DEF_BOOL, OP_CALL
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct Chunk {
|
pub struct Chunk {
|
||||||
|
|
@ -12,8 +12,10 @@ 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>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl Chunk {
|
impl Chunk {
|
||||||
pub fn new(name: &str) -> Chunk {
|
pub fn new(name: &str) -> Chunk {
|
||||||
Chunk {
|
Chunk {
|
||||||
|
|
@ -21,6 +23,7 @@ impl Chunk {
|
||||||
code: Vec::new(),
|
code: Vec::new(),
|
||||||
constants: vec![],
|
constants: vec![],
|
||||||
lines: vec![],
|
lines: vec![],
|
||||||
|
functions: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -34,7 +37,15 @@ impl Chunk {
|
||||||
self.constants.len() - 1
|
self.constants.len() - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add_function(&mut self, function: Chunk) -> usize {
|
||||||
|
self.functions.push(function);
|
||||||
|
self.functions.len() - 1
|
||||||
|
}
|
||||||
|
|
||||||
pub fn disassemble(&self) {
|
pub fn disassemble(&self) {
|
||||||
|
for f in &self.functions{
|
||||||
|
f.disassemble();
|
||||||
|
}
|
||||||
println!("== {} ==", self.name);
|
println!("== {} ==", self.name);
|
||||||
let mut offset = 0;
|
let mut offset = 0;
|
||||||
while offset < self.code.len() {
|
while offset < self.code.len() {
|
||||||
|
|
@ -79,6 +90,7 @@ impl Chunk {
|
||||||
OP_DEF_F32 => self.constant_inst("DEFF32", offset),
|
OP_DEF_F32 => self.constant_inst("DEFF32", offset),
|
||||||
OP_DEF_F64 => self.constant_inst("DEFF64", offset),
|
OP_DEF_F64 => self.constant_inst("DEFF64", offset),
|
||||||
OP_DEF_BOOL => self.constant_inst("DEFBOOL", offset),
|
OP_DEF_BOOL => self.constant_inst("DEFBOOL", offset),
|
||||||
|
OP_CALL => self.call_inst("CALL", offset),
|
||||||
OP_GET => self.constant_inst("GET", offset),
|
OP_GET => self.constant_inst("GET", offset),
|
||||||
_ => {
|
_ => {
|
||||||
println!("Unknown instruction {}", instruction);
|
println!("Unknown instruction {}", instruction);
|
||||||
|
|
@ -92,6 +104,13 @@ impl Chunk {
|
||||||
offset + 1
|
offset + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn call_inst(&self, name: &str, offset: usize) -> usize {
|
||||||
|
let constant = self.code[offset + 1];
|
||||||
|
let num_args = self.code[offset + 2];
|
||||||
|
println!("{} {}({}):", name, constant, num_args);
|
||||||
|
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);
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ impl<'a> Compiler<'a> {
|
||||||
| TokenType::U32
|
| TokenType::U32
|
||||||
| TokenType::U64
|
| TokenType::U64
|
||||||
| TokenType::Date
|
| TokenType::Date
|
||||||
| TokenType::String
|
| TokenType::StringType
|
||||||
| TokenType::Char
|
| TokenType::Char
|
||||||
| TokenType::Bool
|
| TokenType::Bool
|
||||||
| TokenType::ListType
|
| TokenType::ListType
|
||||||
|
|
@ -129,14 +129,14 @@ impl<'a> Compiler<'a> {
|
||||||
Some(TokenType::U32) => OP_DEF_I64,
|
Some(TokenType::U32) => OP_DEF_I64,
|
||||||
Some(TokenType::U64) => OP_DEF_I64,
|
Some(TokenType::U64) => OP_DEF_I64,
|
||||||
Some(TokenType::Date) => OP_DEF_DATE,
|
Some(TokenType::Date) => OP_DEF_DATE,
|
||||||
Some(TokenType::String) => OP_DEF_STRING,
|
Some(TokenType::StringType) => OP_DEF_STRING,
|
||||||
Some(TokenType::Char) => OP_DEF_CHAR,
|
Some(TokenType::Char) => OP_DEF_CHAR,
|
||||||
Some(TokenType::Bool) => OP_DEF_BOOL,
|
Some(TokenType::Bool) => OP_DEF_BOOL,
|
||||||
Some(TokenType::ListType) => OP_DEF_LIST,
|
Some(TokenType::ListType) => OP_DEF_LIST,
|
||||||
Some(TokenType::MapType) => OP_DEF_MAP,
|
Some(TokenType::MapType) => OP_DEF_MAP,
|
||||||
Some(TokenType::Object) => OP_DEF_STRUCT,
|
Some(TokenType::Object) => OP_DEF_STRUCT,
|
||||||
_ => match derived_type {
|
_ => match derived_type {
|
||||||
Some(TokenType::Text) => OP_DEF_STRING,
|
Some(TokenType::StringType) => OP_DEF_STRING,
|
||||||
Some(TokenType::Bool) => OP_DEF_BOOL,
|
Some(TokenType::Bool) => OP_DEF_BOOL,
|
||||||
Some(TokenType::Char) => OP_DEF_CHAR,
|
Some(TokenType::Char) => OP_DEF_CHAR,
|
||||||
Some(TokenType::F64) => OP_DEF_F64,
|
Some(TokenType::F64) => OP_DEF_F64,
|
||||||
|
|
@ -336,8 +336,8 @@ fn literal(s: &mut Compiler, expected_type: Option<TokenType>) -> anyhow::Result
|
||||||
s.typestack.push(TokenType::Bool);
|
s.typestack.push(TokenType::Bool);
|
||||||
s.emit_constant(Value::Bool(true))
|
s.emit_constant(Value::Bool(true))
|
||||||
}
|
}
|
||||||
(TokenType::Text, TokenType::String) => {
|
(TokenType::StringType, TokenType::StringType) => {
|
||||||
s.typestack.push(TokenType::String);
|
s.typestack.push(TokenType::StringType);
|
||||||
s.emit_constant(Value::String(s.previous_token.lexeme.clone()))
|
s.emit_constant(Value::String(s.previous_token.lexeme.clone()))
|
||||||
}
|
}
|
||||||
//list
|
//list
|
||||||
|
|
@ -356,7 +356,7 @@ fn literal(s: &mut Compiler, expected_type: Option<TokenType>) -> anyhow::Result
|
||||||
match actual_type {
|
match actual_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::Text => s.emit_constant(Value::String(s.previous_token.lexeme.clone())),
|
TokenType::StringType => s.emit_constant(Value::String(s.previous_token.lexeme.clone())),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -506,12 +506,12 @@ 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::Text, Rule::new(Some(literal), None, PREC_NONE));
|
rules.insert(TokenType::StringType, 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::String, Rule::new(None, None, PREC_NONE));
|
rules.insert(TokenType::StringType, 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::U32, Rule::new(None, None, PREC_NONE));
|
rules.insert(TokenType::U32, Rule::new(None, None, PREC_NONE));
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ pub(crate) fn get_keyword(lexeme: &str) -> Option<TokenType> {
|
||||||
"object" => Some(TokenType::Object),
|
"object" => Some(TokenType::Object),
|
||||||
"print" => Some(TokenType::Print),
|
"print" => Some(TokenType::Print),
|
||||||
"struct" => Some(TokenType::Struct),
|
"struct" => Some(TokenType::Struct),
|
||||||
"string" => Some(TokenType::String),
|
"string" => Some(TokenType::StringType),
|
||||||
"true" => Some(TokenType::True),
|
"true" => Some(TokenType::True),
|
||||||
"u32" => Some(TokenType::U32),
|
"u32" => Some(TokenType::U32),
|
||||||
"u64" => Some(TokenType::U64),
|
"u64" => Some(TokenType::U64),
|
||||||
|
|
|
||||||
19
src/main.rs
19
src/main.rs
|
|
@ -1,20 +1,25 @@
|
||||||
use crudlang::{ast_compiler, chunk};
|
use crudlang::ast_compiler;
|
||||||
use crudlang::bytecode_compiler::compile;
|
use crudlang::bytecode_compiler::compile;
|
||||||
use crudlang::scanner::scan;
|
use crudlang::scanner::scan;
|
||||||
use crudlang::vm::{interpret, Vm};
|
use crudlang::vm::interpret;
|
||||||
|
|
||||||
fn main() -> anyhow::Result<()> {
|
fn main() -> anyhow::Result<()> {
|
||||||
tracing_subscriber::fmt::init();
|
tracing_subscriber::fmt::init();
|
||||||
|
|
||||||
let tokens = scan(r#"let a = 1>2
|
let tokens = scan(
|
||||||
print a"#);
|
r#"
|
||||||
// println!("{:?}", tokens);
|
fn main(a: string) -> u32:
|
||||||
|
a + 42
|
||||||
|
let text = "hello "
|
||||||
|
main(text)"#,
|
||||||
|
);
|
||||||
|
println!("{:?}", tokens);
|
||||||
match ast_compiler::compile(tokens) {
|
match ast_compiler::compile(tokens) {
|
||||||
Ok(statements) => {
|
Ok(statements) => {
|
||||||
println!("{:?}", statements);
|
println!("{:?}", statements);
|
||||||
let chunk = compile(statements)?;
|
let chunk = compile(&statements)?;
|
||||||
chunk.disassemble();
|
chunk.disassemble();
|
||||||
interpret(chunk);
|
println!("{}",interpret(&chunk)?);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("{}", e)
|
println!("{}", e)
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,14 @@ impl Scanner {
|
||||||
']' => self.add_token(TokenType::RightBracket),
|
']' => self.add_token(TokenType::RightBracket),
|
||||||
',' => self.add_token(TokenType::Comma),
|
',' => self.add_token(TokenType::Comma),
|
||||||
'.' => self.add_token(TokenType::Dot),
|
'.' => self.add_token(TokenType::Dot),
|
||||||
'-' => self.add_token(TokenType::Minus),
|
'-' => {
|
||||||
|
let t = if self.match_next('>') {
|
||||||
|
TokenType::SingleRightArrow
|
||||||
|
} else {
|
||||||
|
TokenType::Minus
|
||||||
|
};
|
||||||
|
self.add_token(t);
|
||||||
|
}
|
||||||
'+' => self.add_token(TokenType::Plus),
|
'+' => self.add_token(TokenType::Plus),
|
||||||
':' => self.add_token(TokenType::Colon),
|
':' => self.add_token(TokenType::Colon),
|
||||||
';' => println!("Warning: Ignoring semicolon at line {}", self.line),
|
';' => println!("Warning: Ignoring semicolon at line {}", self.line),
|
||||||
|
|
@ -180,7 +187,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::Text, value);
|
self.add_token_with_value(TokenType::StringType, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn peek(&self) -> char {
|
fn peek(&self) -> char {
|
||||||
|
|
|
||||||
|
|
@ -78,21 +78,22 @@ pub enum TokenType {
|
||||||
RightBrace,
|
RightBrace,
|
||||||
RightBracket,
|
RightBracket,
|
||||||
Semicolon,
|
Semicolon,
|
||||||
|
SingleRightArrow,
|
||||||
Slash,
|
Slash,
|
||||||
Star,
|
Star,
|
||||||
Text,
|
StringType,
|
||||||
String,
|
|
||||||
Struct,
|
Struct,
|
||||||
True,
|
True,
|
||||||
U32,
|
U32,
|
||||||
U64,
|
U64,
|
||||||
|
Void,
|
||||||
While,
|
While,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for TokenType {
|
impl fmt::Display for TokenType {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
TokenType::String => write!(f, "string"),
|
TokenType::StringType => write!(f, "string"),
|
||||||
TokenType::Date => write!(f, "date"),
|
TokenType::Date => write!(f, "date"),
|
||||||
TokenType::Char => write!(f, "char"),
|
TokenType::Char => write!(f, "char"),
|
||||||
TokenType::I32 => write!(f, "i32"),
|
TokenType::I32 => write!(f, "i32"),
|
||||||
|
|
@ -149,11 +150,12 @@ impl fmt::Display for TokenType {
|
||||||
TokenType::RightBrace => write!(f, "}}"),
|
TokenType::RightBrace => write!(f, "}}"),
|
||||||
TokenType::RightBracket => write!(f, "]"),
|
TokenType::RightBracket => write!(f, "]"),
|
||||||
TokenType::Semicolon => write!(f, ";"),
|
TokenType::Semicolon => write!(f, ";"),
|
||||||
|
TokenType::SingleRightArrow => write!(f, "->"),
|
||||||
TokenType::Slash => write!(f, "/"),
|
TokenType::Slash => write!(f, "/"),
|
||||||
TokenType::Star => write!(f, "*"),
|
TokenType::Star => write!(f, "*"),
|
||||||
TokenType::Text => write!(f, "text"),
|
|
||||||
TokenType::Struct => write!(f, "struct"),
|
TokenType::Struct => write!(f, "struct"),
|
||||||
TokenType::True => write!(f, "true"),
|
TokenType::True => write!(f, "true"),
|
||||||
|
TokenType::Void => write!(f, "()"),
|
||||||
TokenType::While => write!(f, "while"),
|
TokenType::While => write!(f, "while"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
87
src/vm.rs
87
src/vm.rs
|
|
@ -5,8 +5,8 @@ use std::collections::HashMap;
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
macro_rules! define_var {
|
macro_rules! define_var {
|
||||||
($self:ident, $variant:ident) => {{
|
($self:ident, $variant:ident, $chunk:ident) => {{
|
||||||
let name = $self.read_constant();
|
let name = $self.read_name($chunk);
|
||||||
let value = $self.pop();
|
let value = $self.pop();
|
||||||
if let Value::$variant(_) = value {
|
if let Value::$variant(_) = value {
|
||||||
$self.local_vars.insert(name, value);
|
$self.local_vars.insert(name, value);
|
||||||
|
|
@ -20,37 +20,47 @@ macro_rules! define_var {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Vm {
|
pub struct Vm {
|
||||||
chunk: Chunk,
|
|
||||||
ip: usize,
|
ip: usize,
|
||||||
stack: Vec<Value>,
|
stack: Vec<Value>,
|
||||||
local_vars: HashMap<String, Value>,
|
local_vars: HashMap<String, Value>,
|
||||||
error_occurred: bool,
|
error_occurred: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn interpret(chunk: Chunk) -> anyhow::Result<Value> {
|
pub fn interpret(chunk: &Chunk) -> anyhow::Result<Value> {
|
||||||
let mut vm = Vm {
|
let mut vm = Vm {
|
||||||
chunk,
|
|
||||||
ip: 0,
|
ip: 0,
|
||||||
stack: vec![],
|
stack: vec![],
|
||||||
local_vars: HashMap::new(),
|
local_vars: HashMap::new(),
|
||||||
error_occurred: false,
|
error_occurred: false,
|
||||||
};
|
};
|
||||||
vm.run()
|
vm.run(chunk, vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn interpret_function(chunk: &Chunk, args: Vec<Value>) -> anyhow::Result<Value> {
|
||||||
|
let mut vm = Vm {
|
||||||
|
ip: 0,
|
||||||
|
stack: vec![],
|
||||||
|
local_vars: HashMap::new(),
|
||||||
|
error_occurred: false,
|
||||||
|
};
|
||||||
|
vm.run(chunk, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Vm {
|
impl Vm {
|
||||||
fn run(&mut self) -> anyhow::Result<Value> {
|
fn run(&mut self, chunk: &Chunk, args: Vec<Value>) -> anyhow::Result<Value> {
|
||||||
|
for arg in args{
|
||||||
|
self.push(arg);
|
||||||
|
}
|
||||||
loop {
|
loop {
|
||||||
if self.error_occurred {
|
if self.error_occurred {
|
||||||
return Err(anyhow!("Error occurred"));
|
return Err(anyhow!("Error occurred"));
|
||||||
}
|
}
|
||||||
debug!("{:?}", self.stack);
|
debug!("{:?}", self.stack);
|
||||||
let opcode = self.chunk.code[self.ip];
|
let opcode = chunk.code[self.ip];
|
||||||
self.ip += 1;
|
self.ip += 1;
|
||||||
match opcode {
|
match opcode {
|
||||||
OP_CONSTANT => {
|
OP_CONSTANT => {
|
||||||
let value = &self.chunk.constants[self.chunk.code[self.ip] as usize];
|
let value = &chunk.constants[self.read(chunk)];
|
||||||
self.ip += 1;
|
|
||||||
self.push(value.clone());
|
self.push(value.clone());
|
||||||
}
|
}
|
||||||
OP_ADD => binary_op(self, |a, b| a + b),
|
OP_ADD => binary_op(self, |a, b| a + b),
|
||||||
|
|
@ -97,38 +107,55 @@ impl Vm {
|
||||||
println!("{}", v);
|
println!("{}", v);
|
||||||
}
|
}
|
||||||
OP_DEFINE => {
|
OP_DEFINE => {
|
||||||
let name = self.read_constant();
|
let name = self.read_name(chunk);
|
||||||
let value = self.pop();
|
let value = self.pop();
|
||||||
self.local_vars.insert(name, value);
|
self.local_vars.insert(name, value);
|
||||||
}
|
}
|
||||||
OP_DEF_I32 => define_var!(self, I32),
|
OP_DEF_I32 => define_var!(self, I32, chunk),
|
||||||
OP_DEF_I64 => define_var!(self, I64),
|
OP_DEF_I64 => define_var!(self, I64, chunk),
|
||||||
OP_DEF_U32 => define_var!(self, U32),
|
OP_DEF_U32 => define_var!(self, U32, chunk),
|
||||||
OP_DEF_U64 => define_var!(self, U64),
|
OP_DEF_U64 => define_var!(self, U64, chunk),
|
||||||
OP_DEF_F32 => define_var!(self, F32),
|
OP_DEF_F32 => define_var!(self, F32, chunk),
|
||||||
OP_DEF_F64 => define_var!(self, F64),
|
OP_DEF_F64 => define_var!(self, F64, chunk),
|
||||||
OP_DEF_STRING => define_var!(self, String),
|
OP_DEF_STRING => define_var!(self, String, chunk),
|
||||||
OP_DEF_CHAR => define_var!(self, Char),
|
OP_DEF_CHAR => define_var!(self, Char, chunk),
|
||||||
OP_DEF_BOOL => define_var!(self, Bool),
|
OP_DEF_BOOL => define_var!(self, Bool, chunk),
|
||||||
OP_DEF_DATE => define_var!(self, Date),
|
OP_DEF_DATE => define_var!(self, Date, chunk),
|
||||||
OP_DEF_LIST => define_var!(self, List),
|
OP_DEF_LIST => define_var!(self, List, chunk),
|
||||||
OP_DEF_MAP => define_var!(self, Map),
|
OP_DEF_MAP => define_var!(self, Map, chunk),
|
||||||
OP_DEF_STRUCT => define_var!(self, Struct),
|
OP_DEF_STRUCT => define_var!(self, Struct, chunk),
|
||||||
OP_GET => {
|
OP_GET => {
|
||||||
let name = self.read_constant();
|
let name = self.read_name(chunk);
|
||||||
let value = self.local_vars.get(&name).unwrap();
|
let value = self.local_vars.get(&name).unwrap();
|
||||||
self.push(value.clone()); // not happy
|
self.push(value.clone()); // not happy
|
||||||
debug!("after get {:?}", self.stack);
|
debug!("after get {:?}", self.stack);
|
||||||
}
|
}
|
||||||
|
OP_CALL => {
|
||||||
|
let function_index = self.read(chunk);
|
||||||
|
let function = chunk.functions.get(function_index).unwrap();
|
||||||
|
let mut args = vec![];
|
||||||
|
let num_args = self.read(chunk);
|
||||||
|
for _ in 0..num_args {
|
||||||
|
let arg = self.pop();
|
||||||
|
args.push(arg);
|
||||||
|
}
|
||||||
|
args.reverse();
|
||||||
|
let result = interpret_function(function, args)?;
|
||||||
|
self.push(result);
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_constant(&mut self) -> String {
|
fn read(&mut self, chunk: &Chunk) -> usize {
|
||||||
let name = self.chunk.constants[self.chunk.code[self.ip] as usize].to_string();
|
|
||||||
self.ip += 1;
|
self.ip += 1;
|
||||||
name
|
chunk.code[self.ip - 1] as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_name(&mut self, chunk: &Chunk) -> String {
|
||||||
|
let index = self.read(chunk);
|
||||||
|
chunk.constants[index].to_string() //string??
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reset_stack(&mut self) {
|
fn reset_stack(&mut self) {
|
||||||
|
|
@ -177,8 +204,8 @@ pub const OP_DIVIDE: u16 = 5;
|
||||||
pub const OP_NEGATE: u16 = 6;
|
pub const OP_NEGATE: u16 = 6;
|
||||||
pub const OP_PRINT: u16 = 7;
|
pub const OP_PRINT: u16 = 7;
|
||||||
pub const OP_RETURN: u16 = 8;
|
pub const OP_RETURN: u16 = 8;
|
||||||
// pub const OP_TRUE: u16 = 9;
|
pub const OP_CALL: u16 = 9;
|
||||||
// pub const OP_FALSE: u16 = 10; // obsolete, vacant space
|
pub const OP_DEF_FN: u16 = 10;
|
||||||
pub const OP_AND: u16 = 11;
|
pub const OP_AND: u16 = 11;
|
||||||
pub const OP_OR: u16 = 12;
|
pub const OP_OR: u16 = 12;
|
||||||
pub const OP_NOT: u16 = 13;
|
pub const OP_NOT: u16 = 13;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue