function calls, with arguments

This commit is contained in:
Shautvast 2025-10-27 20:31:27 +01:00
parent 50de71572f
commit 84835da814
9 changed files with 365 additions and 111 deletions

View file

@ -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(),
} }
} }
} }

View file

@ -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)?
} }
@ -49,17 +73,36 @@ impl Compiler {
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::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, .. } => { 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,

View file

@ -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);

View file

@ -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));

View file

@ -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),

View file

@ -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)

View file

@ -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 {

View file

@ -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"),
} }
} }

View file

@ -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;