type inference and building the symbol table earlier in the process. Not perfect: duplicated code, no namespaces of the names in the table

This commit is contained in:
Shautvast 2025-11-10 10:42:53 +01:00
parent 9ee8f193aa
commit cbc7f6ce7a
7 changed files with 378 additions and 169 deletions

View file

@ -174,13 +174,14 @@ more date functions will have to follow.
**lists** **lists**
``` ```
let list = ["foo", "bar", 1, 1.0] let list = ["foo", "bar", 1, 1.0]
print(list[1]) list[1]
=> "bar" => "bar"
``` ```
No generic types (yet). A list can hold any type. No generic types (yet). A list can hold any type.
* lists support appending with + * lists support appending with +
``` ```
let list2 = list + "baz" let list2 = list + "baz"
=>["foo", "bar", 1, 1.0, "baz"]
``` ```
_note to self: implement adding 2 lists_ _note to self: implement adding 2 lists_

View file

@ -2,28 +2,30 @@ use crate::ast_compiler::Expression::{
FieldGet, FunctionCall, ListGet, MapGet, NamedParameter, Stop, Variable, FieldGet, FunctionCall, ListGet, MapGet, NamedParameter, Stop, Variable,
}; };
use crate::errors::CompilerError::{ use crate::errors::CompilerError::{
self, Expected, IncompatibleTypes, ParseError, TooManyParameters, TypeError, self, Expected, ParseError, TooManyParameters, UndeclaredVariable, UnexpectedIndent,
UndeclaredVariable, UnexpectedIndent, UninitializedVariable, UninitializedVariable,
}; };
use crate::errors::CompilerErrorAtLine; use crate::errors::CompilerErrorAtLine;
use crate::symbol_builder::{Symbol, calculate_type, infer_type};
use crate::tokens::TokenType::{ use crate::tokens::TokenType::{
Bang, Bool, Char, Colon, DateTime, Dot, Eof, Eol, Equal, F32, F64, False, FloatingPoint, Fn, Bang, Bool, Char, Colon, DateTime, Dot, Eof, Eol, Equal, F32, F64, False, FloatingPoint, Fn,
Greater, GreaterEqual, GreaterGreater, I32, I64, Identifier, Indent, Integer, LeftBrace, Greater, GreaterEqual, GreaterGreater, I32, I64, Identifier, Indent, Integer, LeftBrace,
LeftBracket, LeftParen, Less, LessEqual, LessLess, Let, ListType, MapType, Minus, Object, Plus, LeftBracket, LeftParen, Less, LessEqual, LessLess, Let, ListType, MapType, Minus, Object, Plus,
Print, RightBrace, RightBracket, RightParen, SignedInteger, SingleRightArrow, Slash, Star, Print, RightBrace, RightBracket, RightParen, SignedInteger, SingleRightArrow, Slash, Star,
StringType, True, U32, U64, UnsignedInteger, StringType, True, U32, U64, Unknown, UnsignedInteger,
}; };
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 tokio_postgres::fallible_iterator::FallibleIterator; use std::collections::HashMap;
pub fn compile( pub fn compile(
path: Option<&str>, path: Option<&str>,
tokens: Vec<Token>, tokens: Vec<Token>,
symbol_table: &mut HashMap<String, Symbol>,
) -> Result<Vec<Statement>, CompilerErrorAtLine> { ) -> Result<Vec<Statement>, CompilerErrorAtLine> {
let mut compiler = AstCompiler::new(path.unwrap_or(""), tokens); let mut compiler = AstCompiler::new(path.unwrap_or(""), tokens);
compiler.compile_tokens() compiler.compile_tokens(symbol_table)
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -55,17 +57,23 @@ impl AstCompiler {
self.current = 0; self.current = 0;
} }
fn compile_tokens(&mut self) -> Result<Vec<Statement>, CompilerErrorAtLine> { fn compile_tokens(
&mut self,
symbol_table: &mut HashMap<String, Symbol>,
) -> Result<Vec<Statement>, CompilerErrorAtLine> {
self.reset(); self.reset();
self.compile() self.compile(symbol_table)
} }
fn compile(&mut self) -> Result<Vec<Statement>, CompilerErrorAtLine> { fn compile(
&mut self,
symbol_table: &mut HashMap<String, Symbol>,
) -> Result<Vec<Statement>, CompilerErrorAtLine> {
self.current_line(); self.current_line();
if !self.had_error { if !self.had_error {
let mut statements = vec![]; let mut statements = vec![];
while !self.is_at_end() { while !self.is_at_end() {
let statement = self.indent()?; let statement = self.indent(symbol_table)?;
if let Some(statement) = statement { if let Some(statement) = statement {
statements.push(statement); statements.push(statement);
} else { } else {
@ -83,7 +91,10 @@ impl AstCompiler {
CompilerErrorAtLine::raise(error, self.current_line()) CompilerErrorAtLine::raise(error, self.current_line())
} }
fn indent(&mut self) -> Result<Option<Statement>, CompilerErrorAtLine> { fn indent(
&mut self,
symbol_table: &mut HashMap<String, Symbol>,
) -> Result<Option<Statement>, CompilerErrorAtLine> {
let expected_indent = *self.indent.last().unwrap(); let expected_indent = *self.indent.last().unwrap();
// skip empty lines // skip empty lines
while self.check(Eol) { while self.check(Eol) {
@ -101,39 +112,48 @@ impl AstCompiler {
self.indent.pop(); self.indent.pop();
return Ok(None); return Ok(None);
} else { } else {
Ok(Some(self.declaration()?)) Ok(Some(self.declaration(symbol_table)?))
} }
} }
fn declaration(&mut self) -> Result<Statement, CompilerErrorAtLine> { fn declaration(
&mut self,
symbol_table: &mut HashMap<String, Symbol>,
) -> Result<Statement, CompilerErrorAtLine> {
if self.match_token(vec![Fn]) { if self.match_token(vec![Fn]) {
self.function_declaration() self.function_declaration(symbol_table)
} else if self.match_token(vec![Let]) { } else if self.match_token(vec![Let]) {
self.let_declaration() self.let_declaration(symbol_table)
} else if self.match_token(vec![Object]) { } else if self.match_token(vec![Object]) {
self.object_declaration() self.object_declaration()
} else if self.match_token(vec![TokenType::Pipe]) { } else if self.match_token(vec![TokenType::Pipe]) {
self.guard_declaration() self.guard_declaration(symbol_table)
} else { } else {
self.statement() self.statement(symbol_table)
} }
} }
// | /. -> service.get_all() // | /. -> service.get_all()
// | /{uuid} -> service.get(uuid)? // | /{uuid} -> service.get(uuid)?
// | ?{query.firstname} -> service.get_by_firstname(fname)? // | ?{query.firstname} -> service.get_by_firstname(fname)?
fn guard_declaration(&mut self) -> Result<Statement, CompilerErrorAtLine> { fn guard_declaration(
let if_expr = self.guard_if_expr()?; &mut self,
let then_expr = self.expression()?; symbol_table: &mut HashMap<String, Symbol>,
) -> Result<Statement, CompilerErrorAtLine> {
let if_expr = self.guard_if_expr(symbol_table)?;
let then_expr = self.expression(symbol_table)?;
Ok(Statement::GuardStatement { if_expr, then_expr }) Ok(Statement::GuardStatement { if_expr, then_expr })
} }
fn guard_if_expr(&mut self) -> Result<Expression, CompilerErrorAtLine> { fn guard_if_expr(
&mut self,
symbol_table: &mut HashMap<String, Symbol>,
) -> Result<Expression, CompilerErrorAtLine> {
while !self.check(SingleRightArrow) { while !self.check(SingleRightArrow) {
if self.match_token(vec![Slash]) { if self.match_token(vec![Slash]) {
return self.path_guard_expr(); return self.path_guard_expr();
} else if self.match_token(vec![TokenType::Question]) { } else if self.match_token(vec![TokenType::Question]) {
return self.query_guard_expr(); return self.query_guard_expr(symbol_table);
} else { } else {
return Err(self.raise(Expected("-> or ?"))); return Err(self.raise(Expected("-> or ?")));
} }
@ -143,9 +163,12 @@ impl AstCompiler {
}) })
} }
fn query_guard_expr(&mut self) -> Result<Expression, CompilerErrorAtLine> { fn query_guard_expr(
&mut self,
symbol_table: &mut HashMap<String, Symbol>,
) -> Result<Expression, CompilerErrorAtLine> {
if self.match_token(vec![LeftBrace]) { if self.match_token(vec![LeftBrace]) {
let query_params = self.expression()?; let query_params = self.expression(symbol_table)?;
self.consume(RightBrace, Expected("'}' after guard expression."))?; self.consume(RightBrace, Expected("'}' after guard expression."))?;
Ok(query_params) Ok(query_params)
} else { } else {
@ -211,7 +234,10 @@ impl AstCompiler {
}) })
} }
fn function_declaration(&mut self) -> Result<Statement, CompilerErrorAtLine> { fn function_declaration(
&mut self,
symbol_table: &mut HashMap<String, Symbol>,
) -> Result<Statement, CompilerErrorAtLine> {
let name_token = self.consume(Identifier, Expected("function name."))?; let name_token = self.consume(Identifier, Expected("function name."))?;
self.consume(LeftParen, Expected("'(' after function name."))?; self.consume(LeftParen, Expected("'(' after function name."))?;
let mut parameters = vec![]; let mut parameters = vec![];
@ -246,7 +272,7 @@ impl AstCompiler {
let current_indent = self.indent.last().unwrap(); let current_indent = self.indent.last().unwrap();
self.indent.push(current_indent + 1); self.indent.push(current_indent + 1);
let body = self.compile()?; let body = self.compile(symbol_table)?;
let function = Function { let function = Function {
name: name_token.clone(), name: name_token.clone(),
@ -258,7 +284,10 @@ impl AstCompiler {
Ok(Statement::FunctionStmt { function }) Ok(Statement::FunctionStmt { function })
} }
fn let_declaration(&mut self) -> Result<Statement, CompilerErrorAtLine> { fn let_declaration(
&mut self,
symbol_table: &mut HashMap<String, Symbol>,
) -> Result<Statement, CompilerErrorAtLine> {
if self.peek().token_type.is_type() { if self.peek().token_type.is_type() {
return Err(self.raise(CompilerError::KeywordNotAllowedAsIdentifier( return Err(self.raise(CompilerError::KeywordNotAllowedAsIdentifier(
self.peek().token_type.clone(), self.peek().token_type.clone(),
@ -274,25 +303,24 @@ impl AstCompiler {
}; };
if self.match_token(vec![Equal]) { if self.match_token(vec![Equal]) {
let initializer = self.expression()?; let initializer = self.expression(symbol_table)?;
let declared_type = declared_type.unwrap_or(Unknown);
let inferred_type = initializer.infer_type();
let var_type =
calculate_type(&declared_type, &inferred_type).map_err(|e| self.raise(e))?;
symbol_table.insert(
name_token.lexeme.clone(),
Symbol::Variable {
name: name_token.lexeme.clone(),
var_type: var_type.clone(),
},
);
self.consume(Eol, Expected("end of line after initializer."))?; self.consume(Eol, Expected("end of line after initializer."))?;
// let inferred_type = initializer.infer_type();
// let var_type = match calculate_type(declared_type, inferred_type) {
// Ok(var_type) => var_type,
// Err(e) => {
// self.had_error = true;
// return Err(self.raise(TypeError(Box::new(e))));
// }
// };
// self.vars.push(Variable {
// name: name_token.lexeme.to_string(),
// var_type,
// line: name_token.line,
// });
Ok(Statement::VarStmt { Ok(Statement::VarStmt {
name: name_token, name: name_token,
var_type: declared_type.unwrap_or(TokenType::Unknown), var_type,
initializer, initializer,
}) })
} else { } else {
@ -300,90 +328,141 @@ impl AstCompiler {
} }
} }
fn statement(&mut self) -> Result<Statement, CompilerErrorAtLine> { fn statement(
&mut self,
symbol_table: &mut HashMap<String, Symbol>,
) -> Result<Statement, CompilerErrorAtLine> {
if self.match_token(vec![Print]) { if self.match_token(vec![Print]) {
self.print_statement() self.print_statement(symbol_table)
} else { } else {
self.expr_statement() self.expr_statement(symbol_table)
} }
} }
fn print_statement(&mut self) -> Result<Statement, CompilerErrorAtLine> { fn print_statement(
let expr = self.expression()?; &mut self,
symbol_table: &mut HashMap<String, Symbol>,
) -> Result<Statement, CompilerErrorAtLine> {
let expr = self.expression(symbol_table)?;
self.consume(Eol, Expected("end of line after print statement."))?; self.consume(Eol, Expected("end of line after print statement."))?;
Ok(Statement::PrintStmt { value: expr }) Ok(Statement::PrintStmt { value: expr })
} }
fn expr_statement(&mut self) -> Result<Statement, CompilerErrorAtLine> { fn expr_statement(
let expr = self.expression()?; &mut self,
symbol_table: &mut HashMap<String, Symbol>,
) -> Result<Statement, CompilerErrorAtLine> {
let expr = self.expression(symbol_table)?;
if !self.is_at_end() { if !self.is_at_end() {
self.consume(Eol, Expected("end of line after expression."))?; self.consume(Eol, Expected("end of line after expression."))?;
} }
Ok(Statement::ExpressionStmt { expression: expr }) Ok(Statement::ExpressionStmt { expression: expr })
} }
fn expression(&mut self) -> Result<Expression, CompilerErrorAtLine> { fn expression(
self.or() &mut self,
symbol_table: &mut HashMap<String, Symbol>,
) -> Result<Expression, CompilerErrorAtLine> {
self.or(symbol_table)
} }
fn or(&mut self) -> Result<Expression, CompilerErrorAtLine> { fn or(
let expr = self.and()?; &mut self,
self.binary(vec![TokenType::LogicalOr], expr) symbol_table: &mut HashMap<String, Symbol>,
) -> Result<Expression, CompilerErrorAtLine> {
let expr = self.and(symbol_table)?;
self.binary(vec![TokenType::LogicalOr], expr, symbol_table)
} }
fn and(&mut self) -> Result<Expression, CompilerErrorAtLine> { fn and(
let expr = self.bit_and()?; &mut self,
self.binary(vec![TokenType::LogicalAnd], expr) symbol_table: &mut HashMap<String, Symbol>,
) -> Result<Expression, CompilerErrorAtLine> {
let expr = self.bit_and(symbol_table)?;
self.binary(vec![TokenType::LogicalAnd], expr, symbol_table)
} }
fn bit_and(&mut self) -> Result<Expression, CompilerErrorAtLine> { fn bit_and(
let expr = self.bit_or()?; &mut self,
self.binary(vec![TokenType::BitAnd], expr) symbol_table: &mut HashMap<String, Symbol>,
) -> Result<Expression, CompilerErrorAtLine> {
let expr = self.bit_or(symbol_table)?;
self.binary(vec![TokenType::BitAnd], expr, symbol_table)
} }
fn bit_or(&mut self) -> Result<Expression, CompilerErrorAtLine> { fn bit_or(
let expr = self.bit_xor()?; &mut self,
self.binary(vec![TokenType::Pipe], expr) symbol_table: &mut HashMap<String, Symbol>,
) -> Result<Expression, CompilerErrorAtLine> {
let expr = self.bit_xor(symbol_table)?;
self.binary(vec![TokenType::Pipe], expr, symbol_table)
} }
fn bit_xor(&mut self) -> Result<Expression, CompilerErrorAtLine> { fn bit_xor(
let expr = self.equality()?; &mut self,
self.binary(vec![TokenType::BitXor], expr) symbol_table: &mut HashMap<String, Symbol>,
) -> Result<Expression, CompilerErrorAtLine> {
let expr = self.equality(symbol_table)?;
self.binary(vec![TokenType::BitXor], expr, symbol_table)
} }
fn equality(&mut self) -> Result<Expression, CompilerErrorAtLine> { fn equality(
let expr = self.comparison()?; &mut self,
self.binary(vec![TokenType::EqualEqual, TokenType::BangEqual], expr) symbol_table: &mut HashMap<String, Symbol>,
) -> Result<Expression, CompilerErrorAtLine> {
let expr = self.comparison(symbol_table)?;
self.binary(
vec![TokenType::EqualEqual, TokenType::BangEqual],
expr,
symbol_table,
)
} }
fn comparison(&mut self) -> Result<Expression, CompilerErrorAtLine> { fn comparison(
let expr = self.bitshift()?; &mut self,
self.binary(vec![Greater, GreaterEqual, Less, LessEqual], expr) symbol_table: &mut HashMap<String, Symbol>,
) -> Result<Expression, CompilerErrorAtLine> {
let expr = self.bitshift(symbol_table)?;
self.binary(
vec![Greater, GreaterEqual, Less, LessEqual],
expr,
symbol_table,
)
} }
fn bitshift(&mut self) -> Result<Expression, CompilerErrorAtLine> { fn bitshift(
let expr = self.term()?; &mut self,
self.binary(vec![GreaterGreater, LessLess], expr) symbol_table: &mut HashMap<String, Symbol>,
) -> Result<Expression, CompilerErrorAtLine> {
let expr = self.term(symbol_table)?;
self.binary(vec![GreaterGreater, LessLess], expr, symbol_table)
} }
fn term(&mut self) -> Result<Expression, CompilerErrorAtLine> { fn term(
let expr = self.factor()?; &mut self,
self.binary(vec![Minus, Plus], expr) symbol_table: &mut HashMap<String, Symbol>,
) -> Result<Expression, CompilerErrorAtLine> {
let expr = self.factor(symbol_table)?;
self.binary(vec![Minus, Plus], expr, symbol_table)
} }
fn factor(&mut self) -> Result<Expression, CompilerErrorAtLine> { fn factor(
let expr = self.unary()?; &mut self,
self.binary(vec![Slash, Star], expr) symbol_table: &mut HashMap<String, Symbol>,
) -> Result<Expression, CompilerErrorAtLine> {
let expr = self.unary(symbol_table)?;
self.binary(vec![Slash, Star], expr, symbol_table)
} }
fn binary( fn binary(
&mut self, &mut self,
types: Vec<TokenType>, types: Vec<TokenType>,
mut expr: Expression, mut expr: Expression,
symbol_table: &mut HashMap<String, Symbol>,
) -> Result<Expression, CompilerErrorAtLine> { ) -> Result<Expression, CompilerErrorAtLine> {
while self.match_token(types.clone()) { while self.match_token(types.clone()) {
let operator = self.previous().clone(); let operator = self.previous().clone();
let right = self.comparison()?; let right = self.comparison(symbol_table)?;
expr = Expression::Binary { expr = Expression::Binary {
line: operator.line, line: operator.line,
left: Box::new(expr), left: Box::new(expr),
@ -394,32 +473,37 @@ impl AstCompiler {
Ok(expr) Ok(expr)
} }
fn unary(&mut self) -> Result<Expression, CompilerErrorAtLine> { fn unary(
&mut self,
symbol_table: &mut HashMap<String, Symbol>,
) -> Result<Expression, CompilerErrorAtLine> {
if self.match_token(vec![Bang, Minus]) { if self.match_token(vec![Bang, Minus]) {
let operator = self.previous().clone(); let operator = self.previous().clone();
let right = self.unary()?; let right = self.unary(symbol_table)?;
Ok(Expression::Unary { Ok(Expression::Unary {
line: self.peek().line, line: self.peek().line,
operator, operator,
right: Box::new(right), right: Box::new(right),
}) })
} else { } else {
let expr = self.get(); let expr = self.get(symbol_table);
expr expr
} }
} }
fn get(&mut self) -> Result<Expression, CompilerErrorAtLine> { fn get(
let expr = self.primary()?; &mut self,
symbol_table: &mut HashMap<String, Symbol>,
) -> Result<Expression, CompilerErrorAtLine> {
let expr = self.primary(symbol_table)?;
if self.match_token(vec![LeftParen]) { if self.match_token(vec![LeftParen]) {
let name = self.peek().clone(); let name = self.peek().clone();
self.advance(); self.advance();
self.function_call(name) self.function_call(name, symbol_table)
} else if self.match_token(vec![LeftBracket]) { } else if self.match_token(vec![LeftBracket]) {
let name = self.peek().clone(); let index = self.expression(symbol_table)?;
self.advance(); self.index(expr, index)
self.index(expr, name)
} else if self.match_token(vec![Dot]) { } else if self.match_token(vec![Dot]) {
let name = self.peek().clone(); let name = self.peek().clone();
self.advance(); self.advance();
@ -432,19 +516,27 @@ impl AstCompiler {
fn index( fn index(
&mut self, &mut self,
operand: Expression, operand: Expression,
index: Token, index: Expression,
) -> Result<Expression, CompilerErrorAtLine> { ) -> Result<Expression, CompilerErrorAtLine> {
let get = (match operand { let get = (match &operand {
Expression::Map { .. } => MapGet { Expression::Map { .. } => MapGet {
key: index.lexeme.clone(), key: Box::new(index),
}, },
Expression::List { .. } => ListGet { Expression::List { .. } => ListGet {
list: Box::new(operand), list: Box::new(operand),
index: index.lexeme.clone().parse().map_err(|_| { index: Box::new(index),
self.raise(CompilerError::IllegalTypeToIndex(index.lexeme.clone()))
})?,
}, },
_ => return Err(self.raise(CompilerError::IllegalTypeToIndex("".to_string()))), Variable { var_type, .. } => {
if var_type == &ListType {
ListGet {
list: Box::new(operand),
index: Box::new(index),
}
} else {
return Err(self.raise(CompilerError::IllegalTypeToIndex(var_type.to_string())));
}
}
_ => return Err(self.raise(CompilerError::IllegalTypeToIndex("Unknown".to_string()))),
}); });
self.consume(RightBracket, Expected("']' after index."))?; self.consume(RightBracket, Expected("']' after index."))?;
Ok(get) Ok(get)
@ -461,12 +553,15 @@ impl AstCompiler {
}) })
} }
fn primary(&mut self) -> Result<Expression, CompilerErrorAtLine> { fn primary(
&mut self,
symbol_table: &mut HashMap<String, Symbol>,
) -> Result<Expression, CompilerErrorAtLine> {
debug!("primary {:?}", self.peek()); debug!("primary {:?}", self.peek());
Ok(if self.match_token(vec![LeftBracket]) { Ok(if self.match_token(vec![LeftBracket]) {
self.list()? self.list(symbol_table)?
} else if self.match_token(vec![LeftBrace]) { } else if self.match_token(vec![LeftBrace]) {
self.map()? self.map(symbol_table)?
} else if self.match_token(vec![False]) { } else if self.match_token(vec![False]) {
Expression::Literal { Expression::Literal {
line: self.peek().line, line: self.peek().line,
@ -545,7 +640,7 @@ impl AstCompiler {
), ),
} }
} else if self.match_token(vec![LeftParen]) { } else if self.match_token(vec![LeftParen]) {
let expr = self.expression()?; let expr = self.expression(symbol_table)?;
self.consume(RightParen, Expected("')' after expression."))?; self.consume(RightParen, Expected("')' after expression."))?;
Expression::Grouping { Expression::Grouping {
line: self.peek().line, line: self.peek().line,
@ -556,9 +651,9 @@ impl AstCompiler {
debug!("{:?}", token); debug!("{:?}", token);
// function call? // function call?
if self.match_token(vec![LeftParen]) { if self.match_token(vec![LeftParen]) {
self.function_call(token.clone())? self.function_call(token.clone(), symbol_table)?
} else if self.match_token(vec![Colon]) { } else if self.match_token(vec![Colon]) {
self.named_parameter(&token)? self.named_parameter(&token, symbol_table)?
} else { } else {
// } else if self.check(Dot) { // } else if self.check(Dot) {
// chain of variable or function lookups? // chain of variable or function lookups?
@ -586,13 +681,17 @@ impl AstCompiler {
// } // }
// } else { // } else {
// none of the above, must be a variable lookup // none of the above, must be a variable lookup
self.variable_lookup(&token)? self.variable_lookup(&token, symbol_table)?
} }
}) })
} }
fn named_parameter(&mut self, name: &Token) -> Result<Expression, CompilerErrorAtLine> { fn named_parameter(
let value = self.expression()?; &mut self,
name: &Token,
symbol_table: &mut HashMap<String, Symbol>,
) -> Result<Expression, CompilerErrorAtLine> {
let value = self.expression(symbol_table)?;
let line = name.line; let line = name.line;
Ok(NamedParameter { Ok(NamedParameter {
name: name.clone(), name: name.clone(),
@ -601,10 +700,13 @@ impl AstCompiler {
}) })
} }
fn list(&mut self) -> Result<Expression, CompilerErrorAtLine> { fn list(
&mut self,
symbol_table: &mut HashMap<String, Symbol>,
) -> Result<Expression, CompilerErrorAtLine> {
let mut list = vec![]; let mut list = vec![];
while !self.match_token(vec![RightBracket]) { while !self.match_token(vec![RightBracket]) {
list.push(self.expression()?); list.push(self.expression(symbol_table)?);
if self.peek().token_type == TokenType::Comma { if self.peek().token_type == TokenType::Comma {
self.advance(); self.advance();
} else { } else {
@ -619,12 +721,15 @@ impl AstCompiler {
}) })
} }
fn map(&mut self) -> Result<Expression, CompilerErrorAtLine> { fn map(
&mut self,
symbol_table: &mut HashMap<String, Symbol>,
) -> Result<Expression, CompilerErrorAtLine> {
let mut entries = vec![]; let mut entries = vec![];
while !self.match_token(vec![RightBrace]) { while !self.match_token(vec![RightBrace]) {
let key = self.expression()?; let key = self.expression(symbol_table)?;
self.consume(Colon, Expected("':' after map key."))?; self.consume(Colon, Expected("':' after map key."))?;
let value = self.expression()?; let value = self.expression(symbol_table)?;
entries.push((key, value)); entries.push((key, value));
if self.peek().token_type == TokenType::Comma { if self.peek().token_type == TokenType::Comma {
self.advance(); self.advance();
@ -640,21 +745,35 @@ impl AstCompiler {
}) })
} }
fn variable_lookup(&mut self, name: &Token) -> Result<Expression, CompilerErrorAtLine> { fn variable_lookup(
&mut self,
name: &Token,
symbol_table: &mut HashMap<String, Symbol>,
) -> Result<Expression, CompilerErrorAtLine> {
let var = symbol_table.get(&name.lexeme);
let var_type = if let Some(Symbol::Variable { var_type, .. }) = var {
var_type
} else {
&Unknown
};
Ok(Variable { Ok(Variable {
name: name.lexeme.to_string(), name: name.lexeme.to_string(),
var_type: TokenType::Unknown, var_type: var_type.clone(),
line: name.line, line: name.line,
}) })
} }
fn function_call(&mut self, name: Token) -> Result<Expression, CompilerErrorAtLine> { fn function_call(
&mut self,
name: Token,
symbol_table: &mut HashMap<String, Symbol>,
) -> Result<Expression, CompilerErrorAtLine> {
let mut arguments = vec![]; let mut arguments = vec![];
while !self.match_token(vec![RightParen]) { while !self.match_token(vec![RightParen]) {
if arguments.len() >= 25 { if arguments.len() >= 25 {
return Err(self.raise(TooManyParameters)); return Err(self.raise(TooManyParameters));
} }
let arg = self.expression()?; let arg = self.expression(symbol_table)?;
arguments.push(arg); arguments.push(arg);
if self.peek().token_type == TokenType::Comma { if self.peek().token_type == TokenType::Comma {
self.advance(); self.advance();
@ -826,11 +945,11 @@ pub enum Expression {
value: Box<Expression>, value: Box<Expression>,
}, },
MapGet { MapGet {
key: String, key: Box<Expression>,
}, },
ListGet { ListGet {
list: Box<Expression>, list: Box<Expression>,
index: usize, index: Box<Expression>,
}, },
FieldGet { FieldGet {
field: String, field: String,
@ -857,20 +976,86 @@ impl Expression {
} }
} }
// pub fn get_type(&self) -> &str { pub fn infer_type(&self) -> TokenType {
// match self { match self {
// Expression::Binary { .. } => "binary", Expression::Binary {
// Expression::Unary { .. } => TokenType::Unknown, left,
// Expression::Grouping { .. } => TokenType::Unknown, operator,
// Expression::Literal { literaltype, .. } => literaltype.clone(), right,
// Expression::List { literaltype, .. } => literaltype.clone(), ..
// Expression::Map { literaltype, .. } => literaltype.clone(), } => {
// Expression::Variable { var_type, .. } => var_type.clone(), let left_type = left.infer_type();
// Expression::FunctionCall { .. } => TokenType::Unknown, let right_type = right.infer_type();
// Expression::Stop { .. } => TokenType::Unknown, if vec![Greater, Less, GreaterEqual, LessEqual].contains(&operator.token_type) {
// Expression::NamedParameter { .. } => TokenType::Unknown, Bool
// Expression::MapGet { .. } => TokenType::Unknown, } else if left_type == right_type {
// Expression::ListGet { .. } => TokenType::Unknown, // map to determined numeric type if yet undetermined (32 or 64 bits)
// } match left_type {
// } FloatingPoint => F64,
Integer => I64,
_ => left_type,
}
} else {
if let Plus = operator.token_type {
// includes string concatenation with numbers
// followed by type coercion to 64 bits for numeric types
debug!("coerce {} : {}", left_type, right_type);
match (left_type, right_type) {
(_, StringType) => StringType,
(StringType, _) => StringType,
(FloatingPoint, _) => F64,
(Integer, FloatingPoint) => F64,
(Integer, _) => I64,
(I64, Integer) => I64,
(F64, _) => F64,
(U64, U32) => U64,
(I64, I32) => I64,
// could add a date and a duration. future work
// could add a List and a value. also future work
// could add a Map and a tuple. Will I add tuple types? Future work!
_ => panic!("Unexpected coercion"),
}
// could have done some fall through here, but this will fail less gracefully,
// so if my thinking is wrong or incomplete it will panic
} else {
// type coercion to 64 bits for numeric types
debug!("coerce {} : {}", left_type, right_type);
match (left_type, right_type) {
(FloatingPoint, _) => F64,
(Integer, FloatingPoint) => F64,
(Integer, I64) => I64,
(I64, FloatingPoint) => F64,
(F64, _) => F64,
(U64, U32) => U64,
(I64, I32) => I64,
(I64, Integer) => I64,
_ => panic!("Unexpected coercion"),
}
}
}
}
Expression::Grouping { expression, .. } => expression.infer_type(),
Expression::Literal { literaltype, .. } => literaltype.clone(),
Expression::List { literaltype, .. } => literaltype.clone(),
Expression::Map { literaltype, .. } => literaltype.clone(),
Expression::Unary {
right, operator, ..
} => {
let literal_type = right.infer_type();
if literal_type == Integer && operator.token_type == Minus {
SignedInteger
} else {
UnsignedInteger
}
}
Expression::Variable { var_type, .. } => var_type.clone(),
Expression::Stop { .. } => TokenType::Unknown,
// Expression::PathMatch { .. } => TokenType::Unknown,
Expression::NamedParameter { .. } => TokenType::Unknown,
Expression::ListGet { .. } => TokenType::Unknown,
Expression::MapGet { .. } => TokenType::Unknown,
Expression::FieldGet { .. } => TokenType::Unknown,
FunctionCall { .. } => TokenType::Unknown,
}
}
} }

View file

@ -110,12 +110,11 @@ impl Compiler {
let var = symbols.get(name); let var = symbols.get(name);
if let Some(Symbol::Variable { if let Some(Symbol::Variable {
var_type, var_type,
initializer,
.. ..
}) = var }) = var
{ {
let inferred_type = infer_type(initializer, symbols); let inferred_type = infer_type(initializer, symbols);
let calculated_type = calculate_type(var_type, &inferred_type, symbols) let calculated_type = calculate_type(var_type, &inferred_type)
.map_err(|e| CompilerErrorAtLine::raise(e, statement.line()))?; .map_err(|e| CompilerErrorAtLine::raise(e, statement.line()))?;
if var_type != &Unknown && var_type != &calculated_type { if var_type != &Unknown && var_type != &calculated_type {
return Err(CompilerErrorAtLine::raise( return Err(CompilerErrorAtLine::raise(
@ -298,8 +297,8 @@ impl Compiler {
NamedParameter { .. } => {} NamedParameter { .. } => {}
Expression::ListGet { index, list} => { Expression::ListGet { index, list} => {
self.compile_expression(namespace, list, symbols, registry)?; self.compile_expression(namespace, list, symbols, registry)?;
self.compile_expression(namespace, index, symbols, registry)?;
self.emit_byte(OP_LIST_GET); self.emit_byte(OP_LIST_GET);
self.emit_bytes((index >> 16) as u16, *index as u16);
} }
Expression::MapGet { .. } => {} Expression::MapGet { .. } => {}
Expression::FieldGet { .. } => {} Expression::FieldGet { .. } => {}

View file

@ -33,14 +33,18 @@ pub fn compile_sourcedir(source_dir: &str) -> Result<HashMap<String, Chunk>, Cru
print!("-- Compiling {} -- ", path); print!("-- Compiling {} -- ", path);
let source = fs::read_to_string(path).map_err(map_underlying())?; let source = fs::read_to_string(path).map_err(map_underlying())?;
let tokens = scan(&source)?; let tokens = scan(&source)?;
match ast_compiler::compile(Some(&path), tokens) { let mut symbol_table = HashMap::new();
match ast_compiler::compile(Some(&path), tokens, &mut symbol_table) {
Ok(statements) => { Ok(statements) => {
let path = path.strip_prefix(source_dir).unwrap().replace(".crud", ""); let path = path.strip_prefix(source_dir).unwrap().replace(".crud", "");
let mut symbol_table = HashMap::new();
symbol_builder::build(&path, &statements, &mut symbol_table); symbol_builder::build(&path, &statements, &mut symbol_table);
bytecode_compiler::compile(
bytecode_compiler::compile(Some(&path), &statements, &symbol_table, &mut registry)?; Some(&path),
&statements,
&symbol_table,
&mut registry,
)?;
} }
Err(e) => { Err(e) => {
println!("{}", e); println!("{}", e);
@ -59,8 +63,8 @@ pub fn map_underlying() -> fn(std::io::Error) -> CrudLangError {
pub fn recompile(src: &str, registry: &mut HashMap<String, Chunk>) -> Result<(), CrudLangError> { pub fn recompile(src: &str, registry: &mut HashMap<String, Chunk>) -> Result<(), CrudLangError> {
let tokens = scan(src)?; let tokens = scan(src)?;
let ast = ast_compiler::compile(None, tokens)?;
let mut symbol_table = HashMap::new(); let mut symbol_table = HashMap::new();
let ast = ast_compiler::compile(None, tokens, &mut symbol_table)?;
symbol_builder::build("", &ast, &mut symbol_table); symbol_builder::build("", &ast, &mut symbol_table);
bytecode_compiler::compile(None, &ast, &symbol_table, registry)?; bytecode_compiler::compile(None, &ast, &symbol_table, registry)?;
Ok(()) Ok(())
@ -69,8 +73,8 @@ pub fn recompile(src: &str, registry: &mut HashMap<String, Chunk>) -> Result<(),
pub fn compile(src: &str) -> Result<HashMap<String, Chunk>, CrudLangError> { pub fn compile(src: &str) -> Result<HashMap<String, Chunk>, CrudLangError> {
let tokens = scan(src)?; let tokens = scan(src)?;
let mut registry = HashMap::new(); let mut registry = HashMap::new();
let ast = ast_compiler::compile(None, tokens)?;
let mut symbol_table = HashMap::new(); let mut symbol_table = HashMap::new();
let ast = ast_compiler::compile(None, tokens, &mut symbol_table)?;
symbol_builder::build("", &ast, &mut symbol_table); symbol_builder::build("", &ast, &mut symbol_table);
bytecode_compiler::compile(None, &ast, &symbol_table, &mut registry)?; bytecode_compiler::compile(None, &ast, &symbol_table, &mut registry)?;
Ok(registry) Ok(registry)
@ -78,8 +82,8 @@ pub fn compile(src: &str) -> Result<HashMap<String, Chunk>, CrudLangError> {
pub(crate) fn run(src: &str) -> Result<Value, CrudLangError> { pub(crate) fn run(src: &str) -> Result<Value, CrudLangError> {
let tokens = scan(src)?; let tokens = scan(src)?;
let ast = ast_compiler::compile(None, tokens)?;
let mut symbol_table = HashMap::new(); let mut symbol_table = HashMap::new();
let ast = ast_compiler::compile(None, tokens, &mut symbol_table)?;
symbol_builder::build("", &ast, &mut symbol_table); symbol_builder::build("", &ast, &mut symbol_table);
let mut registry = HashMap::new(); let mut registry = HashMap::new();
bytecode_compiler::compile(None, &ast, &symbol_table, &mut registry)?; bytecode_compiler::compile(None, &ast, &symbol_table, &mut registry)?;

View file

@ -1,8 +1,12 @@
use crate::ast_compiler::{Expression, Parameter, Statement}; use crate::ast_compiler::{Expression, Parameter, Statement};
use crate::errors::CompilerError; use crate::errors::CompilerError;
use crate::errors::CompilerError::{IncompatibleTypes, TypeError}; use crate::errors::CompilerError::{IncompatibleTypes, TypeError};
use crate::tokens::TokenType::{
Bool, DateTime, F32, F64, FloatingPoint, Greater, GreaterEqual, I32, I64, Integer, Less,
LessEqual, ListType, MapType, Minus, Object, Plus, SignedInteger, StringType, U32, U64,
Unknown, UnsignedInteger,
};
use crate::tokens::{Token, TokenType}; use crate::tokens::{Token, TokenType};
use crate::tokens::TokenType::{Bool, F32, F64, FloatingPoint, Greater, GreaterEqual, I32, I64, Integer, Less, LessEqual, ListType, MapType, Minus, Object, Plus, SignedInteger, StringType, U32, U64, Unknown, UnsignedInteger, DateTime};
use log::debug; use log::debug;
use std::collections::HashMap; use std::collections::HashMap;
@ -16,7 +20,6 @@ pub enum Symbol {
Variable { Variable {
name: String, name: String,
var_type: TokenType, var_type: TokenType,
initializer: Expression,
}, },
Object { Object {
name: String, name: String,
@ -40,16 +43,18 @@ pub fn build(path: &str, ast: &[Statement], symbols: &mut HashMap<String, Symbol
var_type, var_type,
initializer, initializer,
} => { } => {
let key = make_qname(path, name);
if !symbols.contains_key(&key) {
// surely there's a better way to do this?
symbols.insert( symbols.insert(
make_qname(path, name), key,
Symbol::Variable { Symbol::Variable {
name: name.lexeme.to_string(), name: name.lexeme.to_string(),
var_type: var_type.clone(), var_type: var_type.clone(),
initializer: initializer.clone(),
}, },
); );
} }
}
Statement::FunctionStmt { function } => { Statement::FunctionStmt { function } => {
symbols.insert( symbols.insert(
make_qname(path, &function.name), make_qname(path, &function.name),
@ -88,7 +93,7 @@ pub fn _add_types(
initializer, initializer,
} => { } => {
let inferred_type = infer_type(initializer, symbols); let inferred_type = infer_type(initializer, symbols);
let calculated_type = calculate_type(var_type, &inferred_type, symbols)?; let calculated_type = calculate_type(var_type, &inferred_type)?;
let entry = symbols.get_mut(&format!("{}.{}", path, name.lexeme)); let entry = symbols.get_mut(&format!("{}.{}", path, name.lexeme));
if let Some(Symbol::Variable { var_type, .. }) = entry { if let Some(Symbol::Variable { var_type, .. }) = entry {
*var_type = calculated_type; *var_type = calculated_type;
@ -103,7 +108,6 @@ pub fn _add_types(
pub fn calculate_type( pub fn calculate_type(
declared_type: &TokenType, declared_type: &TokenType,
inferred_type: &TokenType, inferred_type: &TokenType,
_symbols: &HashMap<String, Symbol>,
) -> Result<TokenType, CompilerError> { ) -> Result<TokenType, CompilerError> {
Ok(if declared_type != &Unknown { Ok(if declared_type != &Unknown {
if declared_type != inferred_type { if declared_type != inferred_type {
@ -118,6 +122,7 @@ pub fn calculate_type(
(F64, FloatingPoint) => F64, (F64, FloatingPoint) => F64,
(U64, I64) => U64, (U64, I64) => U64,
(U64, I32) => U64, (U64, I32) => U64,
(I64, Integer) => I64,
(StringType, _) => StringType, // meh, this all needs rigorous testing. Update: this is in progress (StringType, _) => StringType, // meh, this all needs rigorous testing. Update: this is in progress
_ => { _ => {
return Err(IncompatibleTypes( return Err(IncompatibleTypes(

View file

@ -57,6 +57,18 @@ impl Value {
} }
} }
pub fn cast_usize(self) -> Result<usize, ValueError> {
match self {
Value::U32(v) => Ok(v as usize),
Value::U64(v) => Ok(v as usize),
Value::I32(v) => Ok(v as usize),
Value::I64(v) => Ok(v as usize),
Value::F32(v) => Ok(v as usize),
Value::F64(v) => Ok(v as usize),
_ => Err(ValueError::IllegalCast),
}
}
pub fn cast_i32(self) -> Result<Self, ValueError> { pub fn cast_i32(self) -> Result<Self, ValueError> {
match self { match self {
Value::U32(v) => Ok(Value::I32(v as i32)), Value::U32(v) => Ok(Value::I32(v as i32)),

View file

@ -3,11 +3,11 @@ use crate::errors::RuntimeError::Something;
use crate::errors::{RuntimeError, ValueError}; use crate::errors::{RuntimeError, ValueError};
use crate::tokens::TokenType; use crate::tokens::TokenType;
use crate::value::Value; use crate::value::Value;
use axum::http::{Uri}; use arc_swap::Guard;
use axum::http::Uri;
use reqwest::get;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use arc_swap::Guard;
use reqwest::get;
use tracing::debug; use tracing::debug;
pub struct Vm { pub struct Vm {
@ -18,7 +18,10 @@ pub struct Vm {
registry: Arc<HashMap<String, Chunk>>, registry: Arc<HashMap<String, Chunk>>,
} }
pub fn interpret(registry: Guard<Arc<HashMap<String, Chunk>>>, function: &str) -> Result<Value, RuntimeError> { pub fn interpret(
registry: Guard<Arc<HashMap<String, Chunk>>>,
function: &str,
) -> Result<Value, RuntimeError> {
let chunk = registry.get(function).unwrap().clone(); let chunk = registry.get(function).unwrap().clone();
// for (key,value) in registry.iter() { // for (key,value) in registry.iter() {
// println!("{}", key); // println!("{}", key);
@ -191,12 +194,10 @@ impl Vm {
self.push(value.clone()); // not happy , take ownership, no clone self.push(value.clone()); // not happy , take ownership, no clone
} }
OP_LIST_GET => { OP_LIST_GET => {
let index_high = self.read(chunk); let index = self.pop();
let index_low = self.read(chunk);
let index = (index_high <<16) + index_low;
let list = self.pop(); let list = self.pop();
if let Value::List(list) = list { if let Value::List(list) = list {
self.push(list.get(index).cloned().unwrap()) self.push(list.get(index.cast_usize()?).cloned().unwrap())
} }
} }
OP_CALL => { OP_CALL => {
@ -211,7 +212,9 @@ impl Vm {
args.reverse(); args.reverse();
let function_name = chunk.constants[function_name_index].to_string(); let function_name = chunk.constants[function_name_index].to_string();
let function_chunk = self.registry.get(&function_name) let function_chunk = self
.registry
.get(&function_name)
.or_else(|| self.registry.get(&format!("{}/{}", context, function_name))); .or_else(|| self.registry.get(&format!("{}/{}", context, function_name)));
if function_chunk.is_none() { if function_chunk.is_none() {
return Err(RuntimeError::FunctionNotFound(function_name)); return Err(RuntimeError::FunctionNotFound(function_name));