added a lot of work in progress, but also a symbol table. Means I could refactor a lot of stuff and remove code. The symbol table is a global storage for vars, functions and objects. Helps with type inference and checking on external symbols
This commit is contained in:
parent
2b3b8d4bb3
commit
9b6f265c55
11 changed files with 605 additions and 383 deletions
|
|
@ -112,10 +112,10 @@ fn get(path: string, headers: map, query: map) -> string:
|
||||||
* guards: this will be the way to deal with input
|
* guards: this will be the way to deal with input
|
||||||
```
|
```
|
||||||
fn get() -> [Customer] | Customer? | ():
|
fn get() -> [Customer] | Customer? | ():
|
||||||
| /. -> 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)?
|
||||||
| /.?{query.last_name} -> service.get_by_lastname(lname)?
|
| ?{query.last_name} -> service.get_by_lastname(lname)?
|
||||||
| _ -> 404
|
| _ -> 404
|
||||||
```
|
```
|
||||||
* this may also require ADT's...
|
* this may also require ADT's...
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,6 @@
|
||||||
|
object Person:
|
||||||
|
name: string
|
||||||
|
|
||||||
fn get(path: string) -> string:
|
fn get(path: string) -> string:
|
||||||
service.add("hello", path)
|
let p = Person(name: path)
|
||||||
|
service.add("hello", p.name)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
use crate::ast_compiler::Expression::{FunctionCall, Literal, RemoteFunctionCall, Variable};
|
use crate::ast_compiler::Expression::{
|
||||||
|
FunctionCall, NamedParameter, Stop, Variable,
|
||||||
|
};
|
||||||
use crate::errors::CompilerError::{
|
use crate::errors::CompilerError::{
|
||||||
self, Expected, IncompatibleTypes, ParseError, TooManyParameters, TypeError,
|
self, Expected, IncompatibleTypes, ParseError, TooManyParameters, TypeError,
|
||||||
UndeclaredVariable, UnexpectedIndent, UninitializedVariable,
|
UndeclaredVariable, UnexpectedIndent, UninitializedVariable,
|
||||||
|
|
@ -14,7 +16,6 @@ use crate::tokens::TokenType::{
|
||||||
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(
|
pub fn compile(
|
||||||
path: Option<&str>,
|
path: Option<&str>,
|
||||||
|
|
@ -36,9 +37,7 @@ struct AstCompiler {
|
||||||
tokens: Vec<Token>,
|
tokens: Vec<Token>,
|
||||||
current: usize,
|
current: usize,
|
||||||
had_error: bool,
|
had_error: bool,
|
||||||
vars: Vec<Expression>,
|
|
||||||
indent: Vec<usize>,
|
indent: Vec<usize>,
|
||||||
functions: HashMap<String, Function>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AstCompiler {
|
impl AstCompiler {
|
||||||
|
|
@ -47,9 +46,7 @@ impl AstCompiler {
|
||||||
tokens,
|
tokens,
|
||||||
current: 0,
|
current: 0,
|
||||||
had_error: false,
|
had_error: false,
|
||||||
vars: vec![],
|
|
||||||
indent: vec![0],
|
indent: vec![0],
|
||||||
functions: HashMap::new(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -58,7 +55,6 @@ impl AstCompiler {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_tokens(&mut self) -> Result<Vec<Statement>, CompilerErrorAtLine> {
|
fn compile_tokens(&mut self) -> Result<Vec<Statement>, CompilerErrorAtLine> {
|
||||||
self.collect_functions()?;
|
|
||||||
self.reset();
|
self.reset();
|
||||||
self.compile()
|
self.compile()
|
||||||
}
|
}
|
||||||
|
|
@ -86,59 +82,6 @@ impl AstCompiler {
|
||||||
CompilerErrorAtLine::raise(error, self.current_line())
|
CompilerErrorAtLine::raise(error, self.current_line())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_functions(&mut self) -> Result<(), CompilerErrorAtLine> {
|
|
||||||
while !self.is_at_end() {
|
|
||||||
if self.match_token(vec![Fn]) {
|
|
||||||
let name_token = self.consume(Identifier, Expected("function name."))?;
|
|
||||||
self.consume(LeftParen, Expected("'(' after function name."))?;
|
|
||||||
let mut parameters = vec![];
|
|
||||||
while !self.check(RightParen) {
|
|
||||||
if parameters.len() >= 25 {
|
|
||||||
return Err(self.raise(TooManyParameters));
|
|
||||||
}
|
|
||||||
let parm_name = self.consume(Identifier, Expected("a parameter name."))?;
|
|
||||||
|
|
||||||
self.consume(Colon, Expected(": 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,
|
|
||||||
});
|
|
||||||
if self.peek().token_type == TokenType::Comma {
|
|
||||||
self.advance();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.consume(RightParen, Expected(" ')' after parameters."))?;
|
|
||||||
let return_type = if self.check(SingleRightArrow) {
|
|
||||||
self.consume(SingleRightArrow, Expected("->"))?;
|
|
||||||
self.advance().token_type
|
|
||||||
} else {
|
|
||||||
TokenType::Void
|
|
||||||
};
|
|
||||||
self.consume(Colon, Expected("colon (:) after function declaration."))?;
|
|
||||||
self.consume(Eol, Expected("end of line."))?;
|
|
||||||
|
|
||||||
let function = Function {
|
|
||||||
name: name_token.clone(),
|
|
||||||
parameters,
|
|
||||||
return_type,
|
|
||||||
body: vec![],
|
|
||||||
};
|
|
||||||
|
|
||||||
self.functions.insert(name_token.lexeme, function);
|
|
||||||
} else {
|
|
||||||
self.advance();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn indent(&mut self) -> Result<Option<Statement>, CompilerErrorAtLine> {
|
fn indent(&mut self) -> Result<Option<Statement>, CompilerErrorAtLine> {
|
||||||
let expected_indent = *self.indent.last().unwrap();
|
let expected_indent = *self.indent.last().unwrap();
|
||||||
// skip empty lines
|
// skip empty lines
|
||||||
|
|
@ -168,11 +111,65 @@ impl AstCompiler {
|
||||||
self.let_declaration()
|
self.let_declaration()
|
||||||
} 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]) {
|
||||||
|
self.guard_declaration()
|
||||||
} else {
|
} else {
|
||||||
self.statement()
|
self.statement()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// | /. -> service.get_all()
|
||||||
|
// | /{uuid} -> service.get(uuid)?
|
||||||
|
// | ?{query.firstname} -> service.get_by_firstname(fname)?
|
||||||
|
fn guard_declaration(&mut self) -> Result<Statement, CompilerErrorAtLine> {
|
||||||
|
let if_expr = self.guard_if_expr()?;
|
||||||
|
let then_expr = self.expression()?;
|
||||||
|
Ok(Statement::GuardStatement { if_expr, then_expr })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn guard_if_expr(&mut self) -> Result<Expression, CompilerErrorAtLine> {
|
||||||
|
while !self.check(SingleRightArrow) {
|
||||||
|
if self.match_token(vec![Slash]) {
|
||||||
|
return self.path_guard_expr();
|
||||||
|
} else if self.match_token(vec![TokenType::Question]) {
|
||||||
|
return self.query_guard_expr();
|
||||||
|
} else {
|
||||||
|
return Err(self.raise(Expected("-> or ?")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Stop {
|
||||||
|
line: self.peek().line,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn query_guard_expr(&mut self) -> Result<Expression, CompilerErrorAtLine> {
|
||||||
|
if self.match_token(vec![LeftBrace]) {
|
||||||
|
let query_params = self.expression()?;
|
||||||
|
self.consume(RightBrace, Expected("'}' after guard expression."))?;
|
||||||
|
Ok(query_params)
|
||||||
|
} else {
|
||||||
|
Ok(Stop {
|
||||||
|
line: self.peek().line,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn path_guard_expr(&mut self) -> Result<Expression, CompilerErrorAtLine> {
|
||||||
|
if self.match_token(vec![LeftBrace]) {
|
||||||
|
let path_params = self.match_expression()?;
|
||||||
|
self.consume(RightBrace, Expected("'}' after guard expression."))?;
|
||||||
|
Ok(path_params)
|
||||||
|
} else {
|
||||||
|
Ok(Stop {
|
||||||
|
line: self.peek().line,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn match_expression(&mut self) -> Result<Expression, CompilerErrorAtLine> {
|
||||||
|
Err(self.raise(Expected("unimplemented")))
|
||||||
|
}
|
||||||
|
|
||||||
fn object_declaration(&mut self) -> Result<Statement, CompilerErrorAtLine> {
|
fn object_declaration(&mut self) -> Result<Statement, CompilerErrorAtLine> {
|
||||||
let type_name = self.consume(Identifier, Expected("object name."))?;
|
let type_name = self.consume(Identifier, Expected("object name."))?;
|
||||||
self.consume(Colon, Expected("':' after object name."))?;
|
self.consume(Colon, Expected("':' after object name."))?;
|
||||||
|
|
@ -194,7 +191,7 @@ impl AstCompiler {
|
||||||
if !done {
|
if !done {
|
||||||
let field_name = self.consume(Identifier, Expected("an object field name."))?;
|
let field_name = self.consume(Identifier, Expected("an object field name."))?;
|
||||||
self.consume(Colon, Expected("':' after field name."))?;
|
self.consume(Colon, Expected("':' after field name."))?;
|
||||||
let field_type = self.peek().token_type;
|
let field_type = self.peek().token_type.clone();
|
||||||
if field_type.is_type() {
|
if field_type.is_type() {
|
||||||
self.advance();
|
self.advance();
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -216,40 +213,61 @@ impl AstCompiler {
|
||||||
fn function_declaration(&mut self) -> Result<Statement, CompilerErrorAtLine> {
|
fn function_declaration(&mut self) -> 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![];
|
||||||
while !self.check(RightParen) {
|
while !self.check(RightParen) {
|
||||||
self.advance();
|
if parameters.len() >= 25 {
|
||||||
|
return Err(self.raise(TooManyParameters));
|
||||||
}
|
}
|
||||||
|
let parm_name = self.consume(Identifier, Expected("a parameter name."))?;
|
||||||
|
|
||||||
self.consume(RightParen, Expected("')' after parameters."))?;
|
self.consume(Colon, Expected(": after parameter name"))?;
|
||||||
while !self.check(Colon) {
|
let var_type = self.peek().token_type.clone();
|
||||||
|
|
||||||
|
self.advance();
|
||||||
|
parameters.push(Parameter {
|
||||||
|
name: parm_name,
|
||||||
|
var_type,
|
||||||
|
});
|
||||||
|
if self.peek().token_type == TokenType::Comma {
|
||||||
self.advance();
|
self.advance();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
self.consume(RightParen, Expected(" ')' after parameters."))?;
|
||||||
|
let return_type = if self.check(SingleRightArrow) {
|
||||||
|
self.consume(SingleRightArrow, Expected("->"))?;
|
||||||
|
self.advance().token_type.clone()
|
||||||
|
} else {
|
||||||
|
TokenType::Void
|
||||||
|
};
|
||||||
self.consume(Colon, Expected("colon (:) after function declaration."))?;
|
self.consume(Colon, Expected("colon (:) after function declaration."))?;
|
||||||
self.consume(Eol, Expected("end of line."))?;
|
self.consume(Eol, Expected("end of line."))?;
|
||||||
|
|
||||||
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()?;
|
||||||
|
|
||||||
self.functions.get_mut(&name_token.lexeme).unwrap().body = body;
|
let function = Function {
|
||||||
|
name: name_token.clone(),
|
||||||
let function_stmt = Statement::FunctionStmt {
|
parameters,
|
||||||
function: self.functions.get(&name_token.lexeme).unwrap().clone(),
|
return_type,
|
||||||
|
body,
|
||||||
};
|
};
|
||||||
Ok(function_stmt)
|
|
||||||
|
Ok(Statement::FunctionStmt { function })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn let_declaration(&mut self) -> Result<Statement, CompilerErrorAtLine> {
|
fn let_declaration(&mut self) -> 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,
|
self.peek().token_type.clone(),
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
let name_token = self.consume(Identifier, Expected("variable name."))?;
|
let name_token = self.consume(Identifier, Expected("variable name."))?;
|
||||||
|
|
||||||
let declared_type = if self.check(Colon) {
|
let declared_type = if self.check(Colon) {
|
||||||
self.advance();
|
self.advance();
|
||||||
Some(self.advance().token_type)
|
Some(self.advance().token_type.clone())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
@ -258,22 +276,22 @@ impl AstCompiler {
|
||||||
let initializer = self.expression()?;
|
let initializer = self.expression()?;
|
||||||
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 inferred_type = initializer.infer_type();
|
||||||
let var_type = match calculate_type(declared_type, inferred_type) {
|
// let var_type = match calculate_type(declared_type, inferred_type) {
|
||||||
Ok(var_type) => var_type,
|
// Ok(var_type) => var_type,
|
||||||
Err(e) => {
|
// Err(e) => {
|
||||||
self.had_error = true;
|
// self.had_error = true;
|
||||||
return Err(self.raise(TypeError(Box::new(e))));
|
// return Err(self.raise(TypeError(Box::new(e))));
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
self.vars.push(Expression::Variable {
|
// self.vars.push(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,
|
||||||
});
|
// });
|
||||||
Ok(Statement::VarStmt {
|
Ok(Statement::VarStmt {
|
||||||
name: name_token,
|
name: name_token,
|
||||||
var_type,
|
var_type: declared_type.unwrap_or(TokenType::Unknown),
|
||||||
initializer,
|
initializer,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -324,7 +342,7 @@ impl AstCompiler {
|
||||||
|
|
||||||
fn bit_or(&mut self) -> Result<Expression, CompilerErrorAtLine> {
|
fn bit_or(&mut self) -> Result<Expression, CompilerErrorAtLine> {
|
||||||
let expr = self.bit_xor()?;
|
let expr = self.bit_xor()?;
|
||||||
self.binary(vec![TokenType::BitOr], expr)
|
self.binary(vec![TokenType::Pipe], expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bit_xor(&mut self) -> Result<Expression, CompilerErrorAtLine> {
|
fn bit_xor(&mut self) -> Result<Expression, CompilerErrorAtLine> {
|
||||||
|
|
@ -469,9 +487,13 @@ impl AstCompiler {
|
||||||
} else {
|
} else {
|
||||||
let token = self.advance().clone();
|
let token = self.advance().clone();
|
||||||
debug!("{:?}", token);
|
debug!("{:?}", token);
|
||||||
|
// function call?
|
||||||
if self.match_token(vec![LeftParen]) {
|
if self.match_token(vec![LeftParen]) {
|
||||||
self.function_call(token.lexeme)?
|
self.function_call(token.lexeme)?
|
||||||
|
} else if self.match_token(vec![Colon]) {
|
||||||
|
self.named_parameter(&token)?
|
||||||
} else if self.check(Dot) {
|
} else if self.check(Dot) {
|
||||||
|
// chain of variable or function lookups?
|
||||||
let mut name = "/".to_string();
|
let mut name = "/".to_string();
|
||||||
name.push_str(&self.previous().lexeme);
|
name.push_str(&self.previous().lexeme);
|
||||||
while self.match_token(vec![Dot]) {
|
while self.match_token(vec![Dot]) {
|
||||||
|
|
@ -479,11 +501,13 @@ impl AstCompiler {
|
||||||
name.push_str(&self.peek().lexeme);
|
name.push_str(&self.peek().lexeme);
|
||||||
self.advance();
|
self.advance();
|
||||||
}
|
}
|
||||||
|
// chained function call?
|
||||||
if self.match_token(vec![LeftParen]) {
|
if self.match_token(vec![LeftParen]) {
|
||||||
self.function_call(name)?
|
self.function_call(name)?
|
||||||
} else {
|
} else {
|
||||||
|
// empty line
|
||||||
return if self.match_token(vec![Eol, Eof]) {
|
return if self.match_token(vec![Eol, Eof]) {
|
||||||
Ok(Literal {
|
Ok(Expression::Literal {
|
||||||
value: Value::Void,
|
value: Value::Void,
|
||||||
literaltype: Object,
|
literaltype: Object,
|
||||||
line: token.line,
|
line: token.line,
|
||||||
|
|
@ -493,11 +517,22 @@ impl AstCompiler {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// none of the above, must be a variable lookup
|
||||||
self.variable_lookup(&token)?
|
self.variable_lookup(&token)?
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn named_parameter(&mut self, name: &Token) -> Result<Expression, CompilerErrorAtLine> {
|
||||||
|
let value = self.expression()?;
|
||||||
|
let line = name.line;
|
||||||
|
Ok(NamedParameter {
|
||||||
|
name: name.clone(),
|
||||||
|
value: Box::new(value),
|
||||||
|
line,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn list(&mut self) -> Result<Expression, CompilerErrorAtLine> {
|
fn list(&mut self) -> Result<Expression, CompilerErrorAtLine> {
|
||||||
let mut list = vec![];
|
let mut list = vec![];
|
||||||
while !self.match_token(vec![RightBracket]) {
|
while !self.match_token(vec![RightBracket]) {
|
||||||
|
|
@ -537,57 +572,21 @@ impl AstCompiler {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn variable_lookup(&mut self, token: &Token) -> Result<Expression, CompilerErrorAtLine> {
|
fn variable_lookup(&mut self, name: &Token) -> Result<Expression, CompilerErrorAtLine> {
|
||||||
if let Some((var_name, var_type)) = self
|
|
||||||
.vars
|
|
||||||
.iter()
|
|
||||||
.filter_map(|e| {
|
|
||||||
if let Variable { name, var_type, .. } = e {
|
|
||||||
Some((name, var_type))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.find(|e| e.0 == &token.lexeme)
|
|
||||||
{
|
|
||||||
Ok(Variable {
|
Ok(Variable {
|
||||||
name: var_name.to_string(),
|
name: name.lexeme.to_string(),
|
||||||
var_type: var_type.clone(),
|
var_type: TokenType::Unknown,
|
||||||
line: token.line,
|
line: name.line,
|
||||||
})
|
})
|
||||||
} else {
|
|
||||||
if self.match_token(vec![Dot]) {
|
|
||||||
let right = self.primary()?;
|
|
||||||
self.binary(vec![Dot], right)
|
|
||||||
} else {
|
|
||||||
if self.is_at_end() {
|
|
||||||
Ok(Literal {
|
|
||||||
value: Value::Void,
|
|
||||||
literaltype: Object,
|
|
||||||
line: token.line,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Err(self.raise(UndeclaredVariable(token.lexeme.clone())))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn function_call(&mut self, name: String) -> Result<Expression, CompilerErrorAtLine> {
|
fn function_call(&mut self, name: String) -> Result<Expression, CompilerErrorAtLine> {
|
||||||
if let Some(function) = self.functions.get(&name).cloned() {
|
|
||||||
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()?;
|
||||||
let arg_type = arg.infer_type();
|
|
||||||
if arg_type != function.parameters[arguments.len()].var_type {
|
|
||||||
return Err(self.raise(IncompatibleTypes(
|
|
||||||
function.parameters[arguments.len()].var_type,
|
|
||||||
arg_type,
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
arguments.push(arg);
|
arguments.push(arg);
|
||||||
if self.peek().token_type == TokenType::Comma {
|
if self.peek().token_type == TokenType::Comma {
|
||||||
self.advance();
|
self.advance();
|
||||||
|
|
@ -600,29 +599,7 @@ impl AstCompiler {
|
||||||
line: self.peek().line,
|
line: self.peek().line,
|
||||||
name,
|
name,
|
||||||
arguments,
|
arguments,
|
||||||
return_type: function.return_type,
|
|
||||||
})
|
})
|
||||||
} else {
|
|
||||||
let mut arguments = vec![];
|
|
||||||
while !self.match_token(vec![RightParen]) {
|
|
||||||
if arguments.len() >= 25 {
|
|
||||||
return Err(self.raise(TooManyParameters));
|
|
||||||
}
|
|
||||||
let arg = self.expression()?;
|
|
||||||
arguments.push(arg);
|
|
||||||
if self.peek().token_type == TokenType::Comma {
|
|
||||||
self.advance();
|
|
||||||
} else {
|
|
||||||
self.consume(RightParen, Expected("')' after arguments."))?;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(RemoteFunctionCall {
|
|
||||||
line: self.peek().line,
|
|
||||||
name,
|
|
||||||
arguments,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn consume(
|
fn consume(
|
||||||
|
|
@ -681,45 +658,6 @@ impl AstCompiler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calculate_type(
|
|
||||||
declared_type: Option<TokenType>,
|
|
||||||
inferred_type: TokenType,
|
|
||||||
) -> Result<TokenType, CompilerError> {
|
|
||||||
Ok(if let Some(declared_type) = declared_type {
|
|
||||||
if declared_type != inferred_type {
|
|
||||||
match (declared_type, inferred_type) {
|
|
||||||
(I32, I64) => I32, //need this?
|
|
||||||
(I32, Integer) => I32,
|
|
||||||
(U32, I64) => U32,
|
|
||||||
(U32, Integer) => U32,
|
|
||||||
(F32, F64) => F32,
|
|
||||||
(F32, FloatingPoint) => F32,
|
|
||||||
(F64, I64) => F64,
|
|
||||||
(F64, FloatingPoint) => F64,
|
|
||||||
(U64, I64) => U64,
|
|
||||||
(U64, I32) => U64,
|
|
||||||
(StringType, _) => StringType, // meh, this all needs rigorous testing. Update: this is in progress
|
|
||||||
_ => {
|
|
||||||
return Err(IncompatibleTypes(declared_type, inferred_type));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
declared_type
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
match inferred_type {
|
|
||||||
Integer | I64 => I64,
|
|
||||||
FloatingPoint => F64,
|
|
||||||
Bool => Bool,
|
|
||||||
Date => Date,
|
|
||||||
ListType => ListType,
|
|
||||||
MapType => MapType,
|
|
||||||
Object => Object,
|
|
||||||
_ => return Err(CompilerError::UnexpectedType(inferred_type)),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Statement {
|
pub enum Statement {
|
||||||
ExpressionStmt {
|
ExpressionStmt {
|
||||||
|
|
@ -740,6 +678,10 @@ pub enum Statement {
|
||||||
name: Token,
|
name: Token,
|
||||||
fields: Vec<Parameter>,
|
fields: Vec<Parameter>,
|
||||||
},
|
},
|
||||||
|
GuardStatement {
|
||||||
|
if_expr: Expression,
|
||||||
|
then_expr: Expression,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Statement {
|
impl Statement {
|
||||||
|
|
@ -750,6 +692,7 @@ impl Statement {
|
||||||
Statement::PrintStmt { value } => value.line(),
|
Statement::PrintStmt { value } => value.line(),
|
||||||
Statement::FunctionStmt { function, .. } => function.name.line,
|
Statement::FunctionStmt { function, .. } => function.name.line,
|
||||||
Statement::ObjectStmt { name, .. } => name.line,
|
Statement::ObjectStmt { name, .. } => name.line,
|
||||||
|
Statement::GuardStatement { if_expr, .. } => if_expr.line(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -801,13 +744,18 @@ pub enum Expression {
|
||||||
line: usize,
|
line: usize,
|
||||||
name: String,
|
name: String,
|
||||||
arguments: Vec<Expression>,
|
arguments: Vec<Expression>,
|
||||||
return_type: TokenType,
|
|
||||||
},
|
},
|
||||||
// a remote function call is a function call that is not defined in the current scope
|
Stop {
|
||||||
RemoteFunctionCall {
|
|
||||||
line: usize,
|
line: usize,
|
||||||
name: String,
|
},
|
||||||
arguments: Vec<Expression>,
|
PathMatch {
|
||||||
|
line: usize,
|
||||||
|
condition: Box<Expression>,
|
||||||
|
},
|
||||||
|
NamedParameter {
|
||||||
|
line: usize,
|
||||||
|
name: Token,
|
||||||
|
value: Box<Expression>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -822,85 +770,9 @@ impl Expression {
|
||||||
Self::Map { line, .. } => *line,
|
Self::Map { line, .. } => *line,
|
||||||
Variable { line, .. } => *line,
|
Variable { line, .. } => *line,
|
||||||
FunctionCall { line, .. } => *line,
|
FunctionCall { line, .. } => *line,
|
||||||
RemoteFunctionCall { line, .. } => *line,
|
Stop { line } => *line,
|
||||||
}
|
Expression::PathMatch { line, .. } => *line,
|
||||||
}
|
NamedParameter { line, .. } => *line,
|
||||||
|
|
||||||
pub fn infer_type(&self) -> TokenType {
|
|
||||||
match self {
|
|
||||||
Self::Binary {
|
|
||||||
left,
|
|
||||||
operator,
|
|
||||||
right,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
let left_type = left.infer_type();
|
|
||||||
let right_type = right.infer_type();
|
|
||||||
if vec![Greater, Less, GreaterEqual, LessEqual].contains(&operator.token_type) {
|
|
||||||
Bool
|
|
||||||
} else if left_type == right_type {
|
|
||||||
// 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"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Self::Grouping { expression, .. } => expression.infer_type(),
|
|
||||||
Self::Literal { literaltype, .. } => literaltype.clone(),
|
|
||||||
Self::List { literaltype, .. } => literaltype.clone(),
|
|
||||||
Self::Map { literaltype, .. } => literaltype.clone(),
|
|
||||||
Self::Unary {
|
|
||||||
right, operator, ..
|
|
||||||
} => {
|
|
||||||
let literal_type = right.infer_type();
|
|
||||||
if literal_type == Integer && operator.token_type == Minus {
|
|
||||||
SignedInteger
|
|
||||||
} else {
|
|
||||||
UnsignedInteger
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Variable { var_type, .. } => var_type.clone(),
|
|
||||||
FunctionCall { return_type, .. } => return_type.clone(),
|
|
||||||
RemoteFunctionCall { .. } => TokenType::Unknown,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
use crate::ast_compiler::{Expression, Function, Statement};
|
use crate::ast_compiler::Expression::NamedParameter;
|
||||||
|
use crate::ast_compiler::{Expression, Function, Parameter, Statement};
|
||||||
use crate::chunk::Chunk;
|
use crate::chunk::Chunk;
|
||||||
use crate::errors::CompilerErrorAtLine;
|
use crate::errors::{CompilerError, CompilerErrorAtLine, RuntimeError};
|
||||||
|
use crate::symbol_builder::{Symbol, calculate_type, infer_type};
|
||||||
use crate::tokens::TokenType;
|
use crate::tokens::TokenType;
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
use crate::vm::{
|
use crate::vm::{
|
||||||
|
|
@ -9,17 +11,20 @@ use crate::vm::{
|
||||||
OP_MULTIPLY, OP_NEGATE, OP_NOT, OP_OR, OP_PRINT, OP_RETURN, OP_SHL, OP_SHR, OP_SUBTRACT,
|
OP_MULTIPLY, OP_NEGATE, OP_NOT, OP_OR, OP_PRINT, OP_RETURN, OP_SHL, OP_SHR, OP_SUBTRACT,
|
||||||
};
|
};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use crate::tokens::TokenType::Unknown;
|
||||||
|
|
||||||
pub fn compile(
|
pub fn compile(
|
||||||
qualified_name: Option<&str>,
|
qualified_name: Option<&str>,
|
||||||
ast: &Vec<Statement>,
|
ast: &Vec<Statement>,
|
||||||
|
symbols: &HashMap<String, Symbol>,
|
||||||
registry: &mut HashMap<String, Chunk>,
|
registry: &mut HashMap<String, Chunk>,
|
||||||
) -> Result<(), CompilerErrorAtLine> {
|
) -> Result<(), CompilerErrorAtLine> {
|
||||||
compile_in_namespace(ast, qualified_name, registry)
|
compile_in_namespace(ast, qualified_name, symbols, registry)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn compile_function(
|
pub(crate) fn compile_function(
|
||||||
function: &Function,
|
function: &Function,
|
||||||
|
symbols: &HashMap<String, Symbol>,
|
||||||
registry: &mut HashMap<String, Chunk>,
|
registry: &mut HashMap<String, Chunk>,
|
||||||
namespace: &str,
|
namespace: &str,
|
||||||
) -> Result<Chunk, CompilerErrorAtLine> {
|
) -> Result<Chunk, CompilerErrorAtLine> {
|
||||||
|
|
@ -31,18 +36,20 @@ pub(crate) fn compile_function(
|
||||||
|
|
||||||
compiler.vars.insert(name, var_index);
|
compiler.vars.insert(name, var_index);
|
||||||
}
|
}
|
||||||
|
let mut chunk = compiler.compile(&function.body, symbols, registry, namespace)?;
|
||||||
Ok(compiler.compile(&function.body, registry, namespace)?)
|
chunk.function_parameters = function.parameters.to_vec();
|
||||||
|
Ok(chunk)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn compile_in_namespace(
|
pub(crate) fn compile_in_namespace(
|
||||||
ast: &Vec<Statement>,
|
ast: &Vec<Statement>,
|
||||||
namespace: Option<&str>,
|
namespace: Option<&str>,
|
||||||
|
symbols: &HashMap<String, Symbol>,
|
||||||
registry: &mut HashMap<String, Chunk>,
|
registry: &mut HashMap<String, Chunk>,
|
||||||
) -> Result<(), CompilerErrorAtLine> {
|
) -> Result<(), CompilerErrorAtLine> {
|
||||||
let name = namespace.unwrap_or("main");
|
let name = namespace.unwrap_or("main");
|
||||||
let compiler = Compiler::new(name);
|
let compiler = Compiler::new(name);
|
||||||
let chunk = compiler.compile(ast, registry, name)?;
|
let chunk = compiler.compile(ast, symbols, registry, name)?;
|
||||||
let qname = if let Some(namespace) = namespace {
|
let qname = if let Some(namespace) = namespace {
|
||||||
format!("{}/{}", namespace, "main")
|
format!("{}/{}", namespace, "main")
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -72,11 +79,12 @@ impl Compiler {
|
||||||
fn compile(
|
fn compile(
|
||||||
mut self,
|
mut self,
|
||||||
ast: &Vec<Statement>,
|
ast: &Vec<Statement>,
|
||||||
|
symbols: &HashMap<String, Symbol>,
|
||||||
registry: &mut HashMap<String, Chunk>,
|
registry: &mut HashMap<String, Chunk>,
|
||||||
namespace: &str,
|
namespace: &str,
|
||||||
) -> Result<Chunk, CompilerErrorAtLine> {
|
) -> Result<Chunk, CompilerErrorAtLine> {
|
||||||
for statement in ast {
|
for statement in ast {
|
||||||
self.compile_statement(statement, registry, namespace)?;
|
self.compile_statement(statement, symbols, registry, namespace)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.emit_byte(OP_RETURN);
|
self.emit_byte(OP_RETURN);
|
||||||
|
|
@ -86,6 +94,7 @@ impl Compiler {
|
||||||
fn compile_statement(
|
fn compile_statement(
|
||||||
&mut self,
|
&mut self,
|
||||||
statement: &Statement,
|
statement: &Statement,
|
||||||
|
symbols: &HashMap<String, Symbol>,
|
||||||
registry: &mut HashMap<String, Chunk>,
|
registry: &mut HashMap<String, Chunk>,
|
||||||
namespace: &str,
|
namespace: &str,
|
||||||
) -> Result<(), CompilerErrorAtLine> {
|
) -> Result<(), CompilerErrorAtLine> {
|
||||||
|
|
@ -96,21 +105,44 @@ impl Compiler {
|
||||||
var_type,
|
var_type,
|
||||||
initializer,
|
initializer,
|
||||||
} => {
|
} => {
|
||||||
let name_index = self.chunk.add_var(var_type, &name.lexeme);
|
let name = name.lexeme.as_str();
|
||||||
self.vars.insert(name.lexeme.clone(), name_index);
|
let var = symbols.get(name);
|
||||||
self.compile_expression(namespace, initializer, registry)?;
|
if let Some(Symbol::Variable {
|
||||||
|
var_type,
|
||||||
|
initializer,
|
||||||
|
..
|
||||||
|
}) = var
|
||||||
|
{
|
||||||
|
let inferred_type = infer_type(initializer, symbols);
|
||||||
|
let calculated_type = calculate_type(var_type, &inferred_type, symbols)
|
||||||
|
.map_err(|e| CompilerErrorAtLine::raise(e, statement.line()))?;
|
||||||
|
if var_type != &Unknown && var_type != &calculated_type {
|
||||||
|
return Err(CompilerErrorAtLine::raise(
|
||||||
|
CompilerError::IncompatibleTypes(var_type.clone(), calculated_type),
|
||||||
|
statement.line(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let name_index = self.chunk.add_var(var_type, name);
|
||||||
|
self.vars.insert(name.to_string(), name_index);
|
||||||
|
self.compile_expression(namespace, initializer, symbols, registry)?;
|
||||||
self.emit_bytes(OP_ASSIGN, name_index as u16);
|
self.emit_bytes(OP_ASSIGN, name_index as u16);
|
||||||
|
} else {
|
||||||
|
return Err(CompilerErrorAtLine::raise(
|
||||||
|
CompilerError::UndeclaredVariable(name.to_string()),
|
||||||
|
statement.line(),
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Statement::PrintStmt { value } => {
|
Statement::PrintStmt { value } => {
|
||||||
self.compile_expression(namespace, value, registry)?;
|
self.compile_expression(namespace, value, symbols, registry)?;
|
||||||
self.emit_byte(OP_PRINT);
|
self.emit_byte(OP_PRINT);
|
||||||
}
|
}
|
||||||
Statement::ExpressionStmt { expression } => {
|
Statement::ExpressionStmt { expression } => {
|
||||||
self.compile_expression(namespace, expression, registry)?;
|
self.compile_expression(namespace, expression, symbols, registry)?;
|
||||||
}
|
}
|
||||||
Statement::FunctionStmt { function } => {
|
Statement::FunctionStmt { function } => {
|
||||||
let function_name = function.name.lexeme.clone();
|
let function_name = function.name.lexeme.clone();
|
||||||
let compiled_function = compile_function(function, registry, namespace)?;
|
let compiled_function = compile_function(function, symbols, registry, namespace)?;
|
||||||
registry.insert(
|
registry.insert(
|
||||||
format!("{}/{}", self.chunk.name, function_name),
|
format!("{}/{}", self.chunk.name, function_name),
|
||||||
compiled_function,
|
compiled_function,
|
||||||
|
|
@ -119,6 +151,9 @@ impl Compiler {
|
||||||
Statement::ObjectStmt { name, fields } => {
|
Statement::ObjectStmt { name, fields } => {
|
||||||
self.chunk.add_object_def(&name.lexeme, fields);
|
self.chunk.add_object_def(&name.lexeme, fields);
|
||||||
}
|
}
|
||||||
|
Statement::GuardStatement { .. } => {
|
||||||
|
unimplemented!("guard statement")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -127,37 +162,63 @@ impl Compiler {
|
||||||
&mut self,
|
&mut self,
|
||||||
namespace: &str,
|
namespace: &str,
|
||||||
expression: &Expression,
|
expression: &Expression,
|
||||||
|
symbols: &HashMap<String, Symbol>,
|
||||||
registry: &mut HashMap<String, Chunk>,
|
registry: &mut HashMap<String, Chunk>,
|
||||||
) -> Result<(), CompilerErrorAtLine> {
|
) -> Result<(), CompilerErrorAtLine> {
|
||||||
match expression {
|
match expression {
|
||||||
|
// Expression::FunctionCall {
|
||||||
|
// name, arguments, ..
|
||||||
|
// } => {
|
||||||
|
// let qname = format!("{}.{}", namespace, name);
|
||||||
|
// let name_index = self
|
||||||
|
// .chunk
|
||||||
|
// .find_constant(&qname)
|
||||||
|
// .unwrap_or_else(|| self.chunk.add_constant(Value::String(qname)));
|
||||||
|
//
|
||||||
|
// for argument in arguments {
|
||||||
|
// self.compile_expression(namespace, argument, registry)?;
|
||||||
|
// }
|
||||||
|
// self.emit_bytes(OP_CALL, name_index as u16);
|
||||||
|
// self.emit_byte(arguments.len() as u16);
|
||||||
|
// }
|
||||||
Expression::FunctionCall {
|
Expression::FunctionCall {
|
||||||
name, arguments, ..
|
name, arguments, ..
|
||||||
} => {
|
|
||||||
let qname = format!("{}.{}", namespace, name);
|
|
||||||
let name_index = self
|
|
||||||
.chunk
|
|
||||||
.find_constant(&qname)
|
|
||||||
.unwrap_or_else(|| self.chunk.add_constant(Value::String(qname)));
|
|
||||||
|
|
||||||
for argument in arguments {
|
|
||||||
self.compile_expression(namespace, argument, registry)?;
|
|
||||||
}
|
|
||||||
self.emit_bytes(OP_CALL, name_index as u16);
|
|
||||||
self.emit_byte(arguments.len() as u16);
|
|
||||||
}
|
|
||||||
Expression::RemoteFunctionCall {
|
|
||||||
name, arguments, ..
|
|
||||||
} => {
|
} => {
|
||||||
let name_index = self
|
let name_index = self
|
||||||
.chunk
|
.chunk
|
||||||
.find_constant(&name)
|
.find_constant(&name)
|
||||||
.unwrap_or_else(|| self.chunk.add_constant(Value::String(name.to_string())));
|
.unwrap_or_else(|| self.chunk.add_constant(Value::String(name.to_string())));
|
||||||
|
let function = symbols.get(name);
|
||||||
|
if let Some(Symbol::Function {
|
||||||
|
name,
|
||||||
|
parameters,
|
||||||
|
return_type,
|
||||||
|
body,
|
||||||
|
}) = function
|
||||||
|
{
|
||||||
|
for parameter in parameters {
|
||||||
for argument in arguments {
|
for argument in arguments {
|
||||||
self.compile_expression(namespace, argument, registry)?;
|
if let NamedParameter { name, .. } = argument {
|
||||||
|
if name.lexeme == parameter.name.lexeme {
|
||||||
|
self.compile_expression(
|
||||||
|
namespace, argument, symbols, registry,
|
||||||
|
)?;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.compile_expression(namespace, argument, symbols, registry)?;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self.emit_bytes(OP_CALL, name_index as u16);
|
self.emit_bytes(OP_CALL, name_index as u16);
|
||||||
self.emit_byte(arguments.len() as u16);
|
self.emit_byte(arguments.len() as u16);
|
||||||
|
} else {
|
||||||
|
return Err(CompilerErrorAtLine::raise(
|
||||||
|
CompilerError::FunctionNotFound(name.to_string()),
|
||||||
|
0,
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Expression::Variable { name, .. } => {
|
Expression::Variable { name, .. } => {
|
||||||
let name_index = self.vars.get(name).unwrap();
|
let name_index = self.vars.get(name).unwrap();
|
||||||
|
|
@ -168,24 +229,24 @@ impl Compiler {
|
||||||
}
|
}
|
||||||
Expression::List { values, .. } => {
|
Expression::List { values, .. } => {
|
||||||
for expr in values {
|
for expr in values {
|
||||||
self.compile_expression(namespace, expr, registry)?;
|
self.compile_expression(namespace, expr, symbols, registry)?;
|
||||||
}
|
}
|
||||||
self.emit_bytes(OP_DEF_LIST, values.len() as u16);
|
self.emit_bytes(OP_DEF_LIST, values.len() as u16);
|
||||||
}
|
}
|
||||||
Expression::Map { entries, .. } => {
|
Expression::Map { entries, .. } => {
|
||||||
for (key, value) in entries {
|
for (key, value) in entries {
|
||||||
self.compile_expression(namespace, key, registry)?;
|
self.compile_expression(namespace, key, symbols, registry)?;
|
||||||
self.compile_expression(namespace, value, registry)?;
|
self.compile_expression(namespace, value, symbols, registry)?;
|
||||||
}
|
}
|
||||||
self.emit_bytes(OP_DEF_MAP, entries.len() as u16);
|
self.emit_bytes(OP_DEF_MAP, entries.len() as u16);
|
||||||
}
|
}
|
||||||
Expression::Grouping { expression, .. } => {
|
Expression::Grouping { expression, .. } => {
|
||||||
self.compile_expression(namespace, expression, registry)?
|
self.compile_expression(namespace, expression, symbols, registry)?
|
||||||
}
|
}
|
||||||
Expression::Unary {
|
Expression::Unary {
|
||||||
operator, right, ..
|
operator, right, ..
|
||||||
} => {
|
} => {
|
||||||
self.compile_expression(namespace, right, registry)?;
|
self.compile_expression(namespace, right, symbols, registry)?;
|
||||||
match operator.token_type {
|
match operator.token_type {
|
||||||
TokenType::Minus => {
|
TokenType::Minus => {
|
||||||
self.emit_byte(OP_NEGATE);
|
self.emit_byte(OP_NEGATE);
|
||||||
|
|
@ -202,15 +263,15 @@ impl Compiler {
|
||||||
right,
|
right,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
self.compile_expression(namespace, left, registry)?;
|
self.compile_expression(namespace, left, symbols, registry)?;
|
||||||
self.compile_expression(namespace, right, registry)?;
|
self.compile_expression(namespace, right, symbols, registry)?;
|
||||||
match operator.token_type {
|
match operator.token_type {
|
||||||
TokenType::Plus => self.emit_byte(OP_ADD),
|
TokenType::Plus => self.emit_byte(OP_ADD),
|
||||||
TokenType::Minus => self.emit_byte(OP_SUBTRACT),
|
TokenType::Minus => self.emit_byte(OP_SUBTRACT),
|
||||||
TokenType::Star => self.emit_byte(OP_MULTIPLY),
|
TokenType::Star => self.emit_byte(OP_MULTIPLY),
|
||||||
TokenType::Slash => self.emit_byte(OP_DIVIDE),
|
TokenType::Slash => self.emit_byte(OP_DIVIDE),
|
||||||
TokenType::BitAnd => self.emit_byte(OP_BITAND),
|
TokenType::BitAnd => self.emit_byte(OP_BITAND),
|
||||||
TokenType::BitOr => self.emit_byte(OP_BITOR),
|
TokenType::Pipe => self.emit_byte(OP_BITOR),
|
||||||
TokenType::BitXor => self.emit_byte(OP_BITXOR),
|
TokenType::BitXor => self.emit_byte(OP_BITXOR),
|
||||||
TokenType::GreaterGreater => self.emit_byte(OP_SHR),
|
TokenType::GreaterGreater => self.emit_byte(OP_SHR),
|
||||||
TokenType::LessLess => self.emit_byte(OP_SHL),
|
TokenType::LessLess => self.emit_byte(OP_SHL),
|
||||||
|
|
@ -224,6 +285,9 @@ impl Compiler {
|
||||||
_ => unimplemented!("binary other than plus, minus, star, slash"),
|
_ => unimplemented!("binary other than plus, minus, star, slash"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Expression::Stop { line } => {}
|
||||||
|
Expression::PathMatch { line, .. } => {}
|
||||||
|
Expression::NamedParameter { line, .. } => {}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ pub struct Chunk {
|
||||||
pub constants: Vec<Value>,
|
pub constants: Vec<Value>,
|
||||||
lines: Vec<usize>,
|
lines: Vec<usize>,
|
||||||
object_defs: HashMap<String, Vec<Parameter>>,
|
object_defs: HashMap<String, Vec<Parameter>>,
|
||||||
|
pub(crate) function_parameters: Vec<Parameter>,
|
||||||
pub vars: Vec<(TokenType, String)>
|
pub vars: Vec<(TokenType, String)>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -40,6 +41,7 @@ impl Chunk {
|
||||||
constants: vec![],
|
constants: vec![],
|
||||||
lines: vec![],
|
lines: vec![],
|
||||||
object_defs: HashMap::new(),
|
object_defs: HashMap::new(),
|
||||||
|
function_parameters: vec![],
|
||||||
vars: vec![]
|
vars: vec![]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,19 +25,31 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn literal_list() {
|
fn literal_list() {
|
||||||
assert_eq!(run(r#"["abc","def"]"#), Ok(Value::List(vec![Value::String("abc".into()), Value::String("def".into())])));
|
assert_eq!(
|
||||||
|
run(r#"["abc","def"]"#),
|
||||||
|
Ok(Value::List(vec![
|
||||||
|
Value::String("abc".into()),
|
||||||
|
Value::String("def".into())
|
||||||
|
]))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn infer_type() {
|
fn infer_type() {
|
||||||
assert_eq!(run(r#"let a=1
|
assert_eq!(
|
||||||
a"#), Ok(Value::I64(1)));
|
run(r#"let a=1
|
||||||
|
a"#),
|
||||||
|
Ok(Value::I64(1))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn define_u32() {
|
fn define_u32() {
|
||||||
assert_eq!(run(r#"let a:u32=1
|
assert_eq!(
|
||||||
a"#), Ok(Value::U32(1)));
|
run(r#"let a:u32=1
|
||||||
|
a"#),
|
||||||
|
Ok(Value::U32(1))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -56,7 +68,7 @@ a"#),
|
||||||
if let Err(e) = &r {
|
if let Err(e) = &r {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
e.to_string(),
|
e.to_string(),
|
||||||
"Compilation failed: error at line 1, Type mismatch: Expected u32, found i32/64"
|
"Compilation failed: error at line 1, Expected u32, found i32/64"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -68,7 +80,7 @@ a"#),
|
||||||
if let Err(e) = &r {
|
if let Err(e) = &r {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
e.to_string(),
|
e.to_string(),
|
||||||
"Compilation failed: error at line 1, Type mismatch: Expected u64, found i32/64"
|
"Compilation failed: error at line 1, Expected u64, found i32/64"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -80,7 +92,7 @@ a"#),
|
||||||
if let Err(e) = &r {
|
if let Err(e) = &r {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
e.to_string(),
|
e.to_string(),
|
||||||
"Compilation failed: error at line 1, Type mismatch: Expected u64, found string"
|
"Compilation failed: error at line 1, Expected u64, found string"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -106,18 +118,20 @@ object Person:
|
||||||
assert!(r.is_ok()); // does nothing runtime
|
assert!(r.is_ok()); // does nothing runtime
|
||||||
}
|
}
|
||||||
|
|
||||||
// #[test]
|
// #[test]
|
||||||
// fn object_() {
|
// fn object_() {
|
||||||
// let r = compile(r#"
|
// let r = run(
|
||||||
// object Person:
|
// r#"
|
||||||
// name: string
|
// object Person:
|
||||||
//
|
// name: string
|
||||||
// let p = Person{name: "Sander"}
|
//
|
||||||
// print p
|
// let p = Person(name: "Sander")
|
||||||
// "#, );
|
// print p
|
||||||
// println!("{:?}", r);
|
// "#,
|
||||||
// assert!(r.is_ok());
|
// );
|
||||||
// }
|
// println!("{:?}", r);
|
||||||
|
// assert!(r.is_ok());
|
||||||
|
// }
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn literal_map() {
|
fn literal_map() {
|
||||||
|
|
@ -151,39 +165,54 @@ m"#);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn keyword_error(){
|
fn keyword_error() {
|
||||||
let result = run(r#"let map = {"name": "Dent"}"#);
|
let result = run(r#"let map = {"name": "Dent"}"#);
|
||||||
assert!(result.is_err());
|
assert!(result.is_err());
|
||||||
assert_eq!("Compilation failed: error at line 1, 'map' is a keyword. You cannot use it as an identifier",result.unwrap_err().to_string());
|
assert_eq!(
|
||||||
|
"Compilation failed: error at line 1, 'map' is a keyword. You cannot use it as an identifier",
|
||||||
|
result.unwrap_err().to_string()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add_strings(){
|
fn add_strings() {
|
||||||
assert_eq!(run(r#""a"+"b""#), Ok(Value::String("ab".into())));
|
assert_eq!(run(r#""a"+"b""#), Ok(Value::String("ab".into())));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add_string_and_int(){
|
fn add_string_and_int() {
|
||||||
assert_eq!(run(r#""a"+42"#), Ok(Value::String("a42".into())));
|
assert_eq!(run(r#""a"+42"#), Ok(Value::String("a42".into())));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add_string_and_bool(){
|
fn add_string_and_bool() {
|
||||||
assert_eq!(run(r#""a"+false"#), Ok(Value::String("afalse".into())));
|
assert_eq!(run(r#""a"+false"#), Ok(Value::String("afalse".into())));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add_string_and_scientific_float(){
|
fn add_string_and_scientific_float() {
|
||||||
assert_eq!(run(r#""a"+4.2e10"#), Ok(Value::String("a42000000000".into())));
|
assert_eq!(
|
||||||
|
run(r#""a"+4.2e10"#),
|
||||||
|
Ok(Value::String("a42000000000".into()))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn add_hex_ints(){
|
fn add_hex_ints() {
|
||||||
assert_eq!(run(r#"0x10 + 0x20"#), Ok(Value::U32(48)));
|
assert_eq!(run(r#"0x10 + 0x20"#), Ok(Value::U32(48)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
// #[test]
|
||||||
fn package(){
|
// fn package() {
|
||||||
assert_eq!(run(r#"a.b.c()"#), Ok(Value::U32(48)));
|
// assert_eq!(run(r#"a.b.c()"#), Ok(Value::U32(48)));
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
// #[test]
|
||||||
|
// fn guards() {
|
||||||
|
// assert_eq!(
|
||||||
|
// run(r#"fn get_all_users() -> list:
|
||||||
|
// | /{uuid} -> service.get_by_uuid(uuid)?"#),
|
||||||
|
// Ok(Value::Void)
|
||||||
|
// );
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
24
src/lib.rs
24
src/lib.rs
|
|
@ -19,6 +19,7 @@ pub mod file_watch;
|
||||||
mod keywords;
|
mod keywords;
|
||||||
pub mod repl;
|
pub mod repl;
|
||||||
pub mod scanner;
|
pub mod scanner;
|
||||||
|
mod symbol_builder;
|
||||||
mod tokens;
|
mod tokens;
|
||||||
mod value;
|
mod value;
|
||||||
pub mod vm;
|
pub mod vm;
|
||||||
|
|
@ -34,9 +35,12 @@ pub fn compile_sourcedir(source_dir: &str) -> Result<HashMap<String, Chunk>, Cru
|
||||||
let tokens = scan(&source)?;
|
let tokens = scan(&source)?;
|
||||||
match ast_compiler::compile(Some(&path), tokens) {
|
match ast_compiler::compile(Some(&path), tokens) {
|
||||||
Ok(statements) => {
|
Ok(statements) => {
|
||||||
println!("{}", path);
|
|
||||||
let path = path.strip_prefix(source_dir).unwrap().replace(".crud", "");
|
let path = path.strip_prefix(source_dir).unwrap().replace(".crud", "");
|
||||||
bytecode_compiler::compile(Some(&path), &statements, &mut registry)?;
|
|
||||||
|
let mut symbol_table = HashMap::new();
|
||||||
|
symbol_builder::build(&path, &statements, &mut symbol_table);
|
||||||
|
|
||||||
|
bytecode_compiler::compile(Some(&path), &statements, &symbol_table, &mut registry)?;
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("{}", e);
|
println!("{}", e);
|
||||||
|
|
@ -56,7 +60,9 @@ 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 ast = ast_compiler::compile(None, tokens)?;
|
||||||
bytecode_compiler::compile(None, &ast, registry)?;
|
let mut symbol_table = HashMap::new();
|
||||||
|
symbol_builder::build("", &ast, &mut symbol_table);
|
||||||
|
bytecode_compiler::compile(None, &ast, &symbol_table, registry)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -64,15 +70,19 @@ 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 ast = ast_compiler::compile(None, tokens)?;
|
||||||
bytecode_compiler::compile(None, &ast, &mut registry)?;
|
let mut symbol_table = HashMap::new();
|
||||||
|
symbol_builder::build("", &ast, &mut symbol_table);
|
||||||
|
bytecode_compiler::compile(None, &ast, &symbol_table, &mut registry)?;
|
||||||
Ok(registry)
|
Ok(registry)
|
||||||
}
|
}
|
||||||
|
|
||||||
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 mut registry = HashMap::new();
|
|
||||||
let ast = ast_compiler::compile(None, tokens)?;
|
let ast = ast_compiler::compile(None, tokens)?;
|
||||||
bytecode_compiler::compile(None, &ast, &mut registry)?;
|
let mut symbol_table = HashMap::new();
|
||||||
|
symbol_builder::build("", &ast, &mut symbol_table);
|
||||||
|
let mut registry = HashMap::new();
|
||||||
|
bytecode_compiler::compile(None, &ast, &symbol_table, &mut registry)?;
|
||||||
let registry = ArcSwap::from(Arc::new(registry));
|
let registry = ArcSwap::from(Arc::new(registry));
|
||||||
interpret(registry.load(), "main").map_err(CrudLangError::from)
|
interpret(registry.load(), "main").map_err(CrudLangError::from)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::errors::CompilerError::{IllegalCharLength, UnexpectedIdentifier, Unterminated};
|
use crate::errors::CompilerError::{IllegalCharLength, UnexpectedIdentifier, Unterminated};
|
||||||
use crate::errors::{CompilerError, CompilerErrorAtLine};
|
use crate::errors::{CompilerError, CompilerErrorAtLine};
|
||||||
use crate::tokens::TokenType::{BitXor, FloatingPoint, Integer, U32, U64};
|
use crate::tokens::TokenType::{BitXor, FloatingPoint, Integer, Question, U32, U64};
|
||||||
use crate::{
|
use crate::{
|
||||||
keywords,
|
keywords,
|
||||||
tokens::{
|
tokens::{
|
||||||
|
|
@ -129,11 +129,12 @@ impl Scanner {
|
||||||
let t = if self.match_next('|') {
|
let t = if self.match_next('|') {
|
||||||
TokenType::LogicalOr
|
TokenType::LogicalOr
|
||||||
} else {
|
} else {
|
||||||
TokenType::BitOr
|
TokenType::Pipe
|
||||||
};
|
};
|
||||||
self.add_token(t);
|
self.add_token(t);
|
||||||
}
|
}
|
||||||
'^' => self.add_token(BitXor),
|
'^' => self.add_token(BitXor),
|
||||||
|
'?' => self.add_token(Question),
|
||||||
_ => {
|
_ => {
|
||||||
if c == '0' && self.peek() == 'x' {
|
if c == '0' && self.peek() == 'x' {
|
||||||
self.hex_number()?;
|
self.hex_number()?;
|
||||||
|
|
|
||||||
235
src/symbol_builder.rs
Normal file
235
src/symbol_builder.rs
Normal file
|
|
@ -0,0 +1,235 @@
|
||||||
|
use crate::ast_compiler::{Expression, Parameter, Statement};
|
||||||
|
use crate::errors::CompilerError;
|
||||||
|
use crate::errors::CompilerError::{IncompatibleTypes, TypeError};
|
||||||
|
use crate::tokens::{Token, TokenType};
|
||||||
|
use crate::tokens::TokenType::{
|
||||||
|
Bool, Date, F32, F64, FloatingPoint, Greater, GreaterEqual, I32, I64, Integer, Less, LessEqual,
|
||||||
|
ListType, MapType, Minus, Object, Plus, SignedInteger, StringType, U32, U64, Unknown,
|
||||||
|
UnsignedInteger,
|
||||||
|
};
|
||||||
|
use log::debug;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
pub enum Symbol {
|
||||||
|
Function {
|
||||||
|
name: String,
|
||||||
|
parameters: Vec<Parameter>,
|
||||||
|
return_type: TokenType,
|
||||||
|
body: Vec<Statement>,
|
||||||
|
},
|
||||||
|
Variable {
|
||||||
|
name: String,
|
||||||
|
var_type: TokenType,
|
||||||
|
initializer: Expression,
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
name: String,
|
||||||
|
fields: Vec<Parameter>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_qname(path: &str, name: &Token) -> String {
|
||||||
|
if path == "" {
|
||||||
|
name.lexeme.to_string()
|
||||||
|
} else {
|
||||||
|
format!("{}.{}", path, name.lexeme)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(path: &str, ast: &[Statement], symbols: &mut HashMap<String, Symbol>) {
|
||||||
|
for statement in ast {
|
||||||
|
match statement {
|
||||||
|
Statement::VarStmt {
|
||||||
|
name,
|
||||||
|
var_type,
|
||||||
|
initializer,
|
||||||
|
} => {
|
||||||
|
|
||||||
|
symbols.insert(
|
||||||
|
make_qname(path, name),
|
||||||
|
Symbol::Variable {
|
||||||
|
name: name.lexeme.to_string(),
|
||||||
|
var_type: var_type.clone(),
|
||||||
|
initializer: initializer.clone(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Statement::FunctionStmt { function } => {
|
||||||
|
symbols.insert(
|
||||||
|
make_qname(path, &function.name),
|
||||||
|
Symbol::Function {
|
||||||
|
name: function.name.lexeme.to_string(),
|
||||||
|
parameters: function.parameters.to_vec(),
|
||||||
|
return_type: function.return_type.clone(),
|
||||||
|
body: function.body.to_vec(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Statement::ObjectStmt { name, fields } => {
|
||||||
|
symbols.insert(
|
||||||
|
make_qname(path, name),
|
||||||
|
Symbol::Object {
|
||||||
|
name: name.lexeme.to_string(),
|
||||||
|
fields: fields.to_vec(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_types(
|
||||||
|
path: &str,
|
||||||
|
ast: &[Statement],
|
||||||
|
symbols: &mut HashMap<String, Symbol>,
|
||||||
|
) -> Result<(), CompilerError> {
|
||||||
|
for statement in ast {
|
||||||
|
match statement {
|
||||||
|
Statement::VarStmt {
|
||||||
|
name,
|
||||||
|
var_type,
|
||||||
|
initializer,
|
||||||
|
} => {
|
||||||
|
let inferred_type = infer_type(initializer, symbols);
|
||||||
|
let calculated_type = calculate_type(var_type, &inferred_type, symbols)?;
|
||||||
|
let entry = symbols.get_mut(&format!("{}.{}", path, name.lexeme));
|
||||||
|
if let Some(Symbol::Variable { var_type, .. }) = entry {
|
||||||
|
*var_type = calculated_type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn calculate_type(
|
||||||
|
declared_type: &TokenType,
|
||||||
|
inferred_type: &TokenType,
|
||||||
|
_symbols: &HashMap<String, Symbol>,
|
||||||
|
) -> Result<TokenType, CompilerError> {
|
||||||
|
Ok(if declared_type != &Unknown {
|
||||||
|
if declared_type != inferred_type {
|
||||||
|
match (declared_type, inferred_type) {
|
||||||
|
(I32, I64) => I32, //need this?
|
||||||
|
(I32, Integer) => I32,
|
||||||
|
(U32, I64) => U32,
|
||||||
|
(U32, Integer) => U32,
|
||||||
|
(F32, F64) => F32,
|
||||||
|
(F32, FloatingPoint) => F32,
|
||||||
|
(F64, I64) => F64,
|
||||||
|
(F64, FloatingPoint) => F64,
|
||||||
|
(U64, I64) => U64,
|
||||||
|
(U64, I32) => U64,
|
||||||
|
(StringType, _) => StringType, // meh, this all needs rigorous testing. Update: this is in progress
|
||||||
|
_ => {
|
||||||
|
return Err(IncompatibleTypes(
|
||||||
|
declared_type.clone(),
|
||||||
|
inferred_type.clone(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
declared_type.clone()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
match inferred_type {
|
||||||
|
Integer | I64 => I64,
|
||||||
|
FloatingPoint => F64,
|
||||||
|
Bool => Bool,
|
||||||
|
Date => Date,
|
||||||
|
ListType => ListType,
|
||||||
|
MapType => MapType,
|
||||||
|
Object => Object,
|
||||||
|
_ => return Err(CompilerError::UnexpectedType(inferred_type.clone())),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn infer_type(expr: &Expression, symbols: &HashMap<String, Symbol>) -> TokenType {
|
||||||
|
match expr {
|
||||||
|
Expression::Binary {
|
||||||
|
left,
|
||||||
|
operator,
|
||||||
|
right,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let left_type = infer_type(left, symbols);
|
||||||
|
let right_type = infer_type(right, symbols);
|
||||||
|
if vec![Greater, Less, GreaterEqual, LessEqual].contains(&operator.token_type) {
|
||||||
|
Bool
|
||||||
|
} else if left_type == right_type {
|
||||||
|
// 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, .. } => infer_type(expression, symbols),
|
||||||
|
Expression::Literal { literaltype, .. } => literaltype.clone(),
|
||||||
|
Expression::List { literaltype, .. } => literaltype.clone(),
|
||||||
|
Expression::Map { literaltype, .. } => literaltype.clone(),
|
||||||
|
Expression::Unary {
|
||||||
|
right, operator, ..
|
||||||
|
} => {
|
||||||
|
let literal_type = infer_type(right, symbols);
|
||||||
|
if literal_type == Integer && operator.token_type == Minus {
|
||||||
|
SignedInteger
|
||||||
|
} else {
|
||||||
|
UnsignedInteger
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expression::Variable { var_type, .. } => var_type.clone(),
|
||||||
|
Expression::FunctionCall { name, .. } => {
|
||||||
|
let symbol = symbols.get(name);
|
||||||
|
if let Some(Symbol::Function { return_type, .. }) = symbol {
|
||||||
|
return_type.clone()
|
||||||
|
} else {
|
||||||
|
Unknown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expression::Stop { .. } => TokenType::Unknown,
|
||||||
|
Expression::PathMatch { .. } => TokenType::Unknown,
|
||||||
|
Expression::NamedParameter { .. } => TokenType::Unknown,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -17,12 +17,12 @@ impl Token {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, Copy, Hash)]
|
#[derive(Debug, PartialEq, Clone, Hash)]
|
||||||
pub enum TokenType {
|
pub enum TokenType {
|
||||||
Bang,
|
Bang,
|
||||||
BangEqual,
|
BangEqual,
|
||||||
BitAnd,
|
BitAnd,
|
||||||
BitOr,
|
Pipe,
|
||||||
BitXor,
|
BitXor,
|
||||||
Bool,
|
Bool,
|
||||||
Char,
|
Char,
|
||||||
|
|
@ -71,6 +71,7 @@ pub enum TokenType {
|
||||||
Object,
|
Object,
|
||||||
Plus,
|
Plus,
|
||||||
Print,
|
Print,
|
||||||
|
Question,
|
||||||
Return,
|
Return,
|
||||||
RightParen,
|
RightParen,
|
||||||
RightBrace,
|
RightBrace,
|
||||||
|
|
@ -86,6 +87,7 @@ pub enum TokenType {
|
||||||
Unknown,
|
Unknown,
|
||||||
Void,
|
Void,
|
||||||
While,
|
While,
|
||||||
|
ObjectType(String)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for TokenType {
|
impl fmt::Display for TokenType {
|
||||||
|
|
@ -104,7 +106,7 @@ impl fmt::Display for TokenType {
|
||||||
TokenType::Bang => write!(f, "!"),
|
TokenType::Bang => write!(f, "!"),
|
||||||
TokenType::BangEqual => write!(f, "!="),
|
TokenType::BangEqual => write!(f, "!="),
|
||||||
TokenType::BitAnd => write!(f, "&"),
|
TokenType::BitAnd => write!(f, "&"),
|
||||||
TokenType::BitOr => write!(f, "|"),
|
TokenType::Pipe => write!(f, "|"),
|
||||||
TokenType::BitXor => write!(f, "^"),
|
TokenType::BitXor => write!(f, "^"),
|
||||||
TokenType::Colon => write!(f, ":"),
|
TokenType::Colon => write!(f, ":"),
|
||||||
TokenType::Comma => write!(f, ","),
|
TokenType::Comma => write!(f, ","),
|
||||||
|
|
@ -142,8 +144,10 @@ impl fmt::Display for TokenType {
|
||||||
TokenType::Minus => write!(f, "-"),
|
TokenType::Minus => write!(f, "-"),
|
||||||
TokenType::Not => write!(f, "not"),
|
TokenType::Not => write!(f, "not"),
|
||||||
TokenType::Object => write!(f, "object"),
|
TokenType::Object => write!(f, "object"),
|
||||||
|
TokenType::ObjectType(_) => write!(f, "object"),
|
||||||
TokenType::Plus => write!(f, "+"),
|
TokenType::Plus => write!(f, "+"),
|
||||||
TokenType::Print => write!(f, "print"),
|
TokenType::Print => write!(f, "print"),
|
||||||
|
TokenType::Question => write!(f, "?"),
|
||||||
TokenType::Return => write!(f, "return"),
|
TokenType::Return => write!(f, "return"),
|
||||||
TokenType::RightParen => write!(f, ")"),
|
TokenType::RightParen => write!(f, ")"),
|
||||||
TokenType::RightBrace => write!(f, "}}"),
|
TokenType::RightBrace => write!(f, "}}"),
|
||||||
|
|
@ -158,6 +162,7 @@ impl fmt::Display for TokenType {
|
||||||
TokenType::While => write!(f, "while"),
|
TokenType::While => write!(f, "while"),
|
||||||
TokenType::SignedInteger => write!(f, "i32/64"),
|
TokenType::SignedInteger => write!(f, "i32/64"),
|
||||||
TokenType::UnsignedInteger => write!(f, "u32/64"),
|
TokenType::UnsignedInteger => write!(f, "u32/64"),
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -203,7 +203,7 @@ impl Vm {
|
||||||
|
|
||||||
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));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue