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, 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::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::{Token, TokenType};
|
||||
use crate::value::Value;
|
||||
use log::debug;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub fn compile(tokens: Vec<Token>) -> anyhow::Result<Vec<Statement>> {
|
||||
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 {
|
||||
|
|
@ -19,6 +22,8 @@ struct AstCompiler {
|
|||
current: usize,
|
||||
had_error: bool,
|
||||
vars: Vec<Expression>,
|
||||
indent: Vec<usize>,
|
||||
functions: HashMap<String, Function>,
|
||||
}
|
||||
|
||||
impl AstCompiler {
|
||||
|
|
@ -28,25 +33,109 @@ impl AstCompiler {
|
|||
current: 0,
|
||||
had_error: false,
|
||||
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![];
|
||||
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)
|
||||
}
|
||||
|
||||
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> {
|
||||
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()
|
||||
} else {
|
||||
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> {
|
||||
let name_token = self.consume(Identifier, "Expect variable name.")?;
|
||||
|
||||
|
|
@ -70,7 +159,7 @@ impl AstCompiler {
|
|||
return Err(e);
|
||||
}
|
||||
};
|
||||
self.vars.push(Variable {
|
||||
self.vars.push(Expression::Variable {
|
||||
name: name_token.lexeme.to_string(),
|
||||
var_type,
|
||||
line: name_token.line,
|
||||
|
|
@ -95,8 +184,8 @@ impl AstCompiler {
|
|||
|
||||
fn print_statement(&mut self) -> anyhow::Result<Statement> {
|
||||
let expr = self.expression()?;
|
||||
self.consume(Eol, "Expect end of line after expression.")?;
|
||||
Ok(Statement::Print { value: expr })
|
||||
self.consume(Eol, "Expect end of line after print statement.")?;
|
||||
Ok(Statement::PrintStmt { value: expr })
|
||||
}
|
||||
|
||||
fn expr_statement(&mut self) -> anyhow::Result<Statement> {
|
||||
|
|
@ -192,6 +281,7 @@ impl AstCompiler {
|
|||
}
|
||||
|
||||
fn primary(&mut self) -> anyhow::Result<Expression> {
|
||||
debug!("primary {:?}", self.peek());
|
||||
Ok(if self.match_token(vec![False]) {
|
||||
Expression::Literal {
|
||||
line: self.peek().line,
|
||||
|
|
@ -216,10 +306,10 @@ impl AstCompiler {
|
|||
literaltype: FloatingPoint,
|
||||
value: Value::F64(self.previous().lexeme.parse()?),
|
||||
}
|
||||
} else if self.match_token(vec![Text]) {
|
||||
} else if self.match_token(vec![StringType]) {
|
||||
Expression::Literal {
|
||||
line: self.peek().line,
|
||||
literaltype: Text,
|
||||
literaltype: StringType,
|
||||
value: Value::String(self.previous().lexeme.to_string()),
|
||||
}
|
||||
} else if self.match_token(vec![LeftParen]) {
|
||||
|
|
@ -231,23 +321,67 @@ impl AstCompiler {
|
|||
}
|
||||
} else {
|
||||
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
|
||||
.vars
|
||||
.iter()
|
||||
.filter_map(|e| {
|
||||
if let Variable { name, var_type, .. } = e {
|
||||
if let Expression::Variable { name, var_type, .. } = e {
|
||||
Some((name, var_type))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.find(|e| e.0 == &token.lexeme)
|
||||
.ok_or_else(|| return anyhow::anyhow!("Unknown variable: {}", token.lexeme))?;
|
||||
Variable {
|
||||
.ok_or_else(|| return anyhow::anyhow!("Unknown variable: {:?}", token))?;
|
||||
Ok(Expression::Variable {
|
||||
name: var_name.to_string(),
|
||||
var_type: var_type.clone(),
|
||||
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>,
|
||||
inferred_type: TokenType,
|
||||
) -> anyhow::Result<TokenType> {
|
||||
println!("declared type {:?} inferred type: {:?}", declared_type, inferred_type);
|
||||
Ok(if let Some(declared_type) = declared_type {
|
||||
|
||||
if declared_type != inferred_type {
|
||||
match (declared_type, inferred_type) {
|
||||
(I32, I64) => I32,
|
||||
|
|
@ -312,6 +448,7 @@ fn calculate_type(
|
|||
(F64, I64) => F64,
|
||||
(U64, I64) => U64,
|
||||
(U64, I32) => U64,
|
||||
(StringType, Text) => StringType,
|
||||
_ => {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Incompatible types. Expected {}, found {}",
|
||||
|
|
@ -338,7 +475,7 @@ fn calculate_type(
|
|||
})
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Statement {
|
||||
ExpressionStmt {
|
||||
expression: Expression,
|
||||
|
|
@ -348,26 +485,32 @@ pub enum Statement {
|
|||
var_type: TokenType,
|
||||
initializer: Expression,
|
||||
},
|
||||
Print {
|
||||
PrintStmt {
|
||||
value: Expression,
|
||||
},
|
||||
FunctionStmt {
|
||||
function: Function,
|
||||
},
|
||||
}
|
||||
|
||||
impl Statement {
|
||||
pub fn line(&self) -> usize {
|
||||
match self {
|
||||
Statement::ExpressionStmt { expression } => expression.line(),
|
||||
Statement::VarStmt {
|
||||
name,
|
||||
var_type,
|
||||
initializer,
|
||||
} => name.line,
|
||||
Statement::Print { value } => value.line(),
|
||||
Statement::VarStmt { name, .. } => name.line,
|
||||
Statement::PrintStmt { value } => value.line(),
|
||||
Statement::FunctionStmt { function, .. } => function.name.line,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Parameter {
|
||||
pub(crate) name: Token,
|
||||
pub(crate) var_type: TokenType,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Expression {
|
||||
Binary {
|
||||
line: usize,
|
||||
|
|
@ -394,6 +537,12 @@ pub enum Expression {
|
|||
name: String,
|
||||
var_type: TokenType,
|
||||
},
|
||||
FunctionCall {
|
||||
line: usize,
|
||||
name: String,
|
||||
arguments: Vec<Expression>,
|
||||
return_type: TokenType,
|
||||
},
|
||||
}
|
||||
|
||||
impl Expression {
|
||||
|
|
@ -404,6 +553,7 @@ impl Expression {
|
|||
Self::Grouping { line, expression } => *line,
|
||||
Self::Literal { 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
|
||||
debug!("coerce {} : {}", left_type, right_type);
|
||||
match (left_type, right_type) {
|
||||
(_, Text) => Text,
|
||||
(Text, _) => Text,
|
||||
(_, StringType) => StringType,
|
||||
(StringType, _) => StringType,
|
||||
(FloatingPoint, _) => F64,
|
||||
(Integer, FloatingPoint) => F64,
|
||||
(Integer, _) => I64,
|
||||
|
|
@ -469,6 +619,7 @@ impl Expression {
|
|||
Self::Literal { literaltype, .. } => literaltype.clone(),
|
||||
Self::Unary { right, .. } => right.infer_type(),
|
||||
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, Statement};
|
||||
use crate::ast_compiler::{Expression, Function, Parameter, Statement};
|
||||
use crate::chunk::Chunk;
|
||||
use crate::tokens::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_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> {
|
||||
let compiler = Compiler::new();
|
||||
pub fn compile(ast: &Vec<Statement>) -> anyhow::Result<Chunk> {
|
||||
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)?)
|
||||
}
|
||||
|
||||
|
|
@ -15,20 +37,22 @@ struct Compiler {
|
|||
had_error: bool,
|
||||
current_line: usize,
|
||||
vars: HashMap<String, usize>,
|
||||
functions: HashMap<String, usize>,
|
||||
}
|
||||
|
||||
impl Compiler {
|
||||
fn new() -> Self {
|
||||
fn new(name: &str) -> Self {
|
||||
Self {
|
||||
chunk: Chunk::new("main"),
|
||||
chunk: Chunk::new(name),
|
||||
had_error: false,
|
||||
current_line: 0,
|
||||
vars: HashMap::new(),
|
||||
functions: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn compile(mut self, ast: Vec<Statement>) -> anyhow::Result<Chunk> {
|
||||
for statement in &ast {
|
||||
fn compile(mut self, ast: &Vec<Statement>) -> anyhow::Result<Chunk> {
|
||||
for statement in ast {
|
||||
self.compile_statement(statement)?
|
||||
}
|
||||
|
||||
|
|
@ -49,17 +73,36 @@ impl Compiler {
|
|||
self.compile_expression(initializer)?;
|
||||
self.define_variable(var_type, name_index)?
|
||||
}
|
||||
Statement::Print { value } => {
|
||||
Statement::PrintStmt { value } => {
|
||||
self.compile_expression(value)?;
|
||||
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(())
|
||||
}
|
||||
|
||||
fn compile_expression(&mut self, expression: &Expression) -> anyhow::Result<()> {
|
||||
match expression {
|
||||
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();
|
||||
self.emit_bytes(OP_GET, *name_index as u16);
|
||||
|
|
@ -121,7 +164,7 @@ impl Compiler {
|
|||
TokenType::F32 => OP_DEF_F32,
|
||||
TokenType::F64 => OP_DEF_F64,
|
||||
TokenType::Date => OP_DEF_DATE,
|
||||
TokenType::String => OP_DEF_STRING,
|
||||
TokenType::StringType => OP_DEF_STRING,
|
||||
TokenType::Char => OP_DEF_CHAR,
|
||||
TokenType::Bool => OP_DEF_BOOL,
|
||||
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::vm::{
|
||||
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_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 {
|
||||
|
|
@ -12,8 +12,10 @@ pub struct Chunk {
|
|||
pub code: Vec<u16>,
|
||||
pub constants: Vec<Value>,
|
||||
lines: Vec<usize>,
|
||||
pub(crate) functions: Vec<Chunk>
|
||||
}
|
||||
|
||||
|
||||
impl Chunk {
|
||||
pub fn new(name: &str) -> Chunk {
|
||||
Chunk {
|
||||
|
|
@ -21,6 +23,7 @@ impl Chunk {
|
|||
code: Vec::new(),
|
||||
constants: vec![],
|
||||
lines: vec![],
|
||||
functions: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -34,7 +37,15 @@ impl Chunk {
|
|||
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) {
|
||||
for f in &self.functions{
|
||||
f.disassemble();
|
||||
}
|
||||
println!("== {} ==", self.name);
|
||||
let mut offset = 0;
|
||||
while offset < self.code.len() {
|
||||
|
|
@ -79,6 +90,7 @@ impl Chunk {
|
|||
OP_DEF_F32 => self.constant_inst("DEFF32", offset),
|
||||
OP_DEF_F64 => self.constant_inst("DEFF64", offset),
|
||||
OP_DEF_BOOL => self.constant_inst("DEFBOOL", offset),
|
||||
OP_CALL => self.call_inst("CALL", offset),
|
||||
OP_GET => self.constant_inst("GET", offset),
|
||||
_ => {
|
||||
println!("Unknown instruction {}", instruction);
|
||||
|
|
@ -92,6 +104,13 @@ impl Chunk {
|
|||
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 {
|
||||
let constant = self.code[offset + 1];
|
||||
print!("{} {}:", name, constant);
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ impl<'a> Compiler<'a> {
|
|||
| TokenType::U32
|
||||
| TokenType::U64
|
||||
| TokenType::Date
|
||||
| TokenType::String
|
||||
| TokenType::StringType
|
||||
| TokenType::Char
|
||||
| TokenType::Bool
|
||||
| TokenType::ListType
|
||||
|
|
@ -129,14 +129,14 @@ impl<'a> Compiler<'a> {
|
|||
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::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::Text) => OP_DEF_STRING,
|
||||
Some(TokenType::StringType) => OP_DEF_STRING,
|
||||
Some(TokenType::Bool) => OP_DEF_BOOL,
|
||||
Some(TokenType::Char) => OP_DEF_CHAR,
|
||||
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.emit_constant(Value::Bool(true))
|
||||
}
|
||||
(TokenType::Text, TokenType::String) => {
|
||||
s.typestack.push(TokenType::String);
|
||||
(TokenType::StringType, TokenType::StringType) => {
|
||||
s.typestack.push(TokenType::StringType);
|
||||
s.emit_constant(Value::String(s.previous_token.lexeme.clone()))
|
||||
}
|
||||
//list
|
||||
|
|
@ -356,7 +356,7 @@ fn literal(s: &mut Compiler, expected_type: Option<TokenType>) -> anyhow::Result
|
|||
match actual_type {
|
||||
TokenType::False => s.emit_constant(Value::Bool(false)),
|
||||
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::Slash, 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(
|
||||
TokenType::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::True, Rule::new(Some(literal), 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),
|
||||
"print" => Some(TokenType::Print),
|
||||
"struct" => Some(TokenType::Struct),
|
||||
"string" => Some(TokenType::String),
|
||||
"string" => Some(TokenType::StringType),
|
||||
"true" => Some(TokenType::True),
|
||||
"u32" => Some(TokenType::U32),
|
||||
"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::scanner::scan;
|
||||
use crudlang::vm::{interpret, Vm};
|
||||
use crudlang::vm::interpret;
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
tracing_subscriber::fmt::init();
|
||||
|
||||
let tokens = scan(r#"let a = 1>2
|
||||
print a"#);
|
||||
// println!("{:?}", tokens);
|
||||
let tokens = scan(
|
||||
r#"
|
||||
fn main(a: string) -> u32:
|
||||
a + 42
|
||||
let text = "hello "
|
||||
main(text)"#,
|
||||
);
|
||||
println!("{:?}", tokens);
|
||||
match ast_compiler::compile(tokens) {
|
||||
Ok(statements) => {
|
||||
println!("{:?}", statements);
|
||||
let chunk = compile(statements)?;
|
||||
let chunk = compile(&statements)?;
|
||||
chunk.disassemble();
|
||||
interpret(chunk);
|
||||
println!("{}",interpret(&chunk)?);
|
||||
}
|
||||
Err(e) => {
|
||||
println!("{}", e)
|
||||
|
|
|
|||
|
|
@ -48,7 +48,14 @@ impl Scanner {
|
|||
']' => self.add_token(TokenType::RightBracket),
|
||||
',' => self.add_token(TokenType::Comma),
|
||||
'.' => 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::Colon),
|
||||
';' => 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]
|
||||
.iter()
|
||||
.collect();
|
||||
self.add_token_with_value(TokenType::Text, value);
|
||||
self.add_token_with_value(TokenType::StringType, value);
|
||||
}
|
||||
|
||||
fn peek(&self) -> char {
|
||||
|
|
|
|||
|
|
@ -78,21 +78,22 @@ pub enum TokenType {
|
|||
RightBrace,
|
||||
RightBracket,
|
||||
Semicolon,
|
||||
SingleRightArrow,
|
||||
Slash,
|
||||
Star,
|
||||
Text,
|
||||
String,
|
||||
StringType,
|
||||
Struct,
|
||||
True,
|
||||
U32,
|
||||
U64,
|
||||
Void,
|
||||
While,
|
||||
}
|
||||
|
||||
impl fmt::Display for TokenType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
TokenType::String => write!(f, "string"),
|
||||
TokenType::StringType => write!(f, "string"),
|
||||
TokenType::Date => write!(f, "date"),
|
||||
TokenType::Char => write!(f, "char"),
|
||||
TokenType::I32 => write!(f, "i32"),
|
||||
|
|
@ -149,11 +150,12 @@ impl fmt::Display for TokenType {
|
|||
TokenType::RightBrace => write!(f, "}}"),
|
||||
TokenType::RightBracket => write!(f, "]"),
|
||||
TokenType::Semicolon => write!(f, ";"),
|
||||
TokenType::SingleRightArrow => 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::Void => write!(f, "()"),
|
||||
TokenType::While => write!(f, "while"),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
87
src/vm.rs
87
src/vm.rs
|
|
@ -5,8 +5,8 @@ use std::collections::HashMap;
|
|||
use tracing::debug;
|
||||
|
||||
macro_rules! define_var {
|
||||
($self:ident, $variant:ident) => {{
|
||||
let name = $self.read_constant();
|
||||
($self:ident, $variant:ident, $chunk:ident) => {{
|
||||
let name = $self.read_name($chunk);
|
||||
let value = $self.pop();
|
||||
if let Value::$variant(_) = value {
|
||||
$self.local_vars.insert(name, value);
|
||||
|
|
@ -20,37 +20,47 @@ macro_rules! define_var {
|
|||
}
|
||||
|
||||
pub struct Vm {
|
||||
chunk: Chunk,
|
||||
ip: usize,
|
||||
stack: Vec<Value>,
|
||||
local_vars: HashMap<String, Value>,
|
||||
error_occurred: bool,
|
||||
}
|
||||
|
||||
pub fn interpret(chunk: Chunk) -> anyhow::Result<Value> {
|
||||
pub fn interpret(chunk: &Chunk) -> anyhow::Result<Value> {
|
||||
let mut vm = Vm {
|
||||
chunk,
|
||||
ip: 0,
|
||||
stack: vec![],
|
||||
local_vars: HashMap::new(),
|
||||
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 {
|
||||
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 {
|
||||
if self.error_occurred {
|
||||
return Err(anyhow!("Error occurred"));
|
||||
}
|
||||
debug!("{:?}", self.stack);
|
||||
let opcode = self.chunk.code[self.ip];
|
||||
let opcode = chunk.code[self.ip];
|
||||
self.ip += 1;
|
||||
match opcode {
|
||||
OP_CONSTANT => {
|
||||
let value = &self.chunk.constants[self.chunk.code[self.ip] as usize];
|
||||
self.ip += 1;
|
||||
let value = &chunk.constants[self.read(chunk)];
|
||||
self.push(value.clone());
|
||||
}
|
||||
OP_ADD => binary_op(self, |a, b| a + b),
|
||||
|
|
@ -97,38 +107,55 @@ impl Vm {
|
|||
println!("{}", v);
|
||||
}
|
||||
OP_DEFINE => {
|
||||
let name = self.read_constant();
|
||||
let name = self.read_name(chunk);
|
||||
let value = self.pop();
|
||||
self.local_vars.insert(name, value);
|
||||
}
|
||||
OP_DEF_I32 => define_var!(self, I32),
|
||||
OP_DEF_I64 => define_var!(self, I64),
|
||||
OP_DEF_U32 => define_var!(self, U32),
|
||||
OP_DEF_U64 => define_var!(self, U64),
|
||||
OP_DEF_F32 => define_var!(self, F32),
|
||||
OP_DEF_F64 => define_var!(self, F64),
|
||||
OP_DEF_STRING => define_var!(self, String),
|
||||
OP_DEF_CHAR => define_var!(self, Char),
|
||||
OP_DEF_BOOL => define_var!(self, Bool),
|
||||
OP_DEF_DATE => define_var!(self, Date),
|
||||
OP_DEF_LIST => define_var!(self, List),
|
||||
OP_DEF_MAP => define_var!(self, Map),
|
||||
OP_DEF_STRUCT => define_var!(self, Struct),
|
||||
OP_DEF_I32 => define_var!(self, I32, chunk),
|
||||
OP_DEF_I64 => define_var!(self, I64, chunk),
|
||||
OP_DEF_U32 => define_var!(self, U32, chunk),
|
||||
OP_DEF_U64 => define_var!(self, U64, chunk),
|
||||
OP_DEF_F32 => define_var!(self, F32, chunk),
|
||||
OP_DEF_F64 => define_var!(self, F64, chunk),
|
||||
OP_DEF_STRING => define_var!(self, String, chunk),
|
||||
OP_DEF_CHAR => define_var!(self, Char, chunk),
|
||||
OP_DEF_BOOL => define_var!(self, Bool, chunk),
|
||||
OP_DEF_DATE => define_var!(self, Date, chunk),
|
||||
OP_DEF_LIST => define_var!(self, List, chunk),
|
||||
OP_DEF_MAP => define_var!(self, Map, chunk),
|
||||
OP_DEF_STRUCT => define_var!(self, Struct, chunk),
|
||||
OP_GET => {
|
||||
let name = self.read_constant();
|
||||
let name = self.read_name(chunk);
|
||||
let value = self.local_vars.get(&name).unwrap();
|
||||
self.push(value.clone()); // not happy
|
||||
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 {
|
||||
let name = self.chunk.constants[self.chunk.code[self.ip] as usize].to_string();
|
||||
fn read(&mut self, chunk: &Chunk) -> usize {
|
||||
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) {
|
||||
|
|
@ -177,8 +204,8 @@ pub const OP_DIVIDE: u16 = 5;
|
|||
pub const OP_NEGATE: u16 = 6;
|
||||
pub const OP_PRINT: u16 = 7;
|
||||
pub const OP_RETURN: u16 = 8;
|
||||
// pub const OP_TRUE: u16 = 9;
|
||||
// pub const OP_FALSE: u16 = 10; // obsolete, vacant space
|
||||
pub const OP_CALL: u16 = 9;
|
||||
pub const OP_DEF_FN: u16 = 10;
|
||||
pub const OP_AND: u16 = 11;
|
||||
pub const OP_OR: u16 = 12;
|
||||
pub const OP_NOT: u16 = 13;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue