WIP if expression. Moved print from statement to builtin
This commit is contained in:
parent
f858ccf6a4
commit
408d772247
6 changed files with 127 additions and 87 deletions
21
README.md
21
README.md
|
|
@ -202,6 +202,22 @@ fn add(a:i64, b:i64) -> i64:
|
|||
let sum = add(1,2)
|
||||
```
|
||||
|
||||
**for loops**
|
||||
```
|
||||
for i in [1,2,3]:
|
||||
print(i)
|
||||
```
|
||||
|
||||
**if / else expressions **
|
||||
```
|
||||
let a = if true:
|
||||
42
|
||||
else:
|
||||
0
|
||||
print(a)
|
||||
```
|
||||
=>42
|
||||
|
||||
**An actual controller**
|
||||
```
|
||||
fn get() -> string:
|
||||
|
|
@ -214,7 +230,4 @@ fn add(a: string, b: string) -> string:
|
|||
|
||||
ISSUES
|
||||
* Make everything an expression. If is a statement and so it can not be type checked
|
||||
* improve indenting
|
||||
* add an extra pass that creates a more IR-like representation (enum instead of bytes)
|
||||
* easier debugging
|
||||
* chunk debug to stdout can be removed
|
||||
* improve indenting
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
use crate::builtins::{FunctionMap, Signature, add};
|
||||
use crate::compiler::tokens::TokenType::DateTime;
|
||||
use crate::compiler::tokens::TokenType::{DateTime, Void};
|
||||
use crate::errors::RuntimeError;
|
||||
use crate::value::Value;
|
||||
use std::collections::HashMap;
|
||||
|
|
@ -9,10 +9,25 @@ pub(crate) static GLOBAL_FUNCTIONS: LazyLock<FunctionMap> = LazyLock::new(|| {
|
|||
let mut global_functions: FunctionMap = HashMap::new();
|
||||
let functions = &mut global_functions;
|
||||
add(functions, "now", Signature::new(vec![], DateTime, now));
|
||||
add(functions, "print", Signature::new(vec![], Void, print));
|
||||
add(functions, "println", Signature::new(vec![], Void, println));
|
||||
|
||||
global_functions
|
||||
});
|
||||
|
||||
fn println(_self_val: Value, args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||
print(_self_val, args)?;
|
||||
println!();
|
||||
Ok(Value::Void)
|
||||
}
|
||||
|
||||
fn print(_self_val: Value, args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||
for arg in args {
|
||||
print!("{}", arg);
|
||||
}
|
||||
Ok(Value::Void)
|
||||
}
|
||||
|
||||
fn now(_self_val: Value, _args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||
Ok(Value::DateTime(Box::new(chrono::DateTime::from(
|
||||
chrono::Utc::now(),
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use crate::compiler::assembly_pass::Op::{
|
|||
Dup, Equal, Get, Goto, GotoIf, GotoIfNot, Greater, GreaterEqual, Less, LessEqual, ListGet,
|
||||
Multiply, Negate, Not, NotEqual, Or, Pop, Print, Return, Shr, Subtract,
|
||||
};
|
||||
use crate::compiler::ast_pass::Expression::NamedParameter;
|
||||
use crate::compiler::ast_pass::Expression::{IfExpression, NamedParameter};
|
||||
use crate::compiler::ast_pass::{Expression, Function, Parameter, Statement};
|
||||
use crate::compiler::tokens::TokenType;
|
||||
use crate::compiler::tokens::TokenType::Unknown;
|
||||
|
|
@ -213,31 +213,6 @@ impl AsmPass {
|
|||
Statement::GuardStatement { .. } => {
|
||||
unimplemented!("guard statement")
|
||||
}
|
||||
Statement::IfStatement {
|
||||
condition,
|
||||
then_branch,
|
||||
else_branch,
|
||||
} => {
|
||||
self.compile_expression(namespace, condition, symbols, registry)?;
|
||||
|
||||
self.emit(Dup);
|
||||
self.emit(GotoIfNot(0)); // placeholder
|
||||
let goto_addr1 = self.chunk.code.len() - 1;
|
||||
self.emit(Pop);
|
||||
self.compile_statements(then_branch, symbols, registry, namespace)?;
|
||||
self.emit(Goto(0));
|
||||
let goto_addr2 = self.chunk.code.len() - 1; // placeholder
|
||||
self.chunk.code[goto_addr1] = GotoIfNot(self.chunk.code.len());
|
||||
if else_branch.is_some() {
|
||||
self.compile_statements(
|
||||
else_branch.as_ref().unwrap(),
|
||||
symbols,
|
||||
registry,
|
||||
namespace,
|
||||
)?;
|
||||
}
|
||||
self.chunk.code[goto_addr2] = Op::Goto(self.chunk.code.len());
|
||||
}
|
||||
Statement::ForStatement {
|
||||
loop_var,
|
||||
range,
|
||||
|
|
@ -282,6 +257,31 @@ impl AsmPass {
|
|||
registry: &mut AsmRegistry,
|
||||
) -> Result<(), CompilerErrorAtLine> {
|
||||
match expression {
|
||||
IfExpression {
|
||||
condition,
|
||||
then_branch,
|
||||
else_branch,
|
||||
} => {
|
||||
self.compile_expression(namespace, condition, symbols, registry)?;
|
||||
|
||||
self.emit(Dup);
|
||||
self.emit(GotoIfNot(0)); // placeholder
|
||||
let goto_addr1 = self.chunk.code.len() - 1;
|
||||
self.emit(Pop);
|
||||
self.compile_statements(then_branch, symbols, registry, namespace)?;
|
||||
self.emit(Goto(0));
|
||||
let goto_addr2 = self.chunk.code.len() - 1; // placeholder
|
||||
self.chunk.code[goto_addr1] = GotoIfNot(self.chunk.code.len());
|
||||
if else_branch.is_some() {
|
||||
self.compile_statements(
|
||||
else_branch.as_ref().unwrap(),
|
||||
symbols,
|
||||
registry,
|
||||
namespace,
|
||||
)?;
|
||||
}
|
||||
self.chunk.code[goto_addr2] = Op::Goto(self.chunk.code.len());
|
||||
}
|
||||
Expression::FunctionCall {
|
||||
name, arguments, ..
|
||||
} => {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
use crate::builtins::globals::GLOBAL_FUNCTIONS;
|
||||
use crate::compiler::ast_pass::Expression::{
|
||||
Assignment, FieldGet, FunctionCall, ListGet, MapGet, MethodCall, NamedParameter, Stop, Variable,
|
||||
Assignment, FieldGet, FunctionCall, IfExpression, ListGet, MapGet, MethodCall, NamedParameter,
|
||||
Stop, Variable,
|
||||
};
|
||||
use crate::compiler::tokens::TokenType::{
|
||||
Bang, Bool, Char, Colon, DateTime, Dot, Else, Eof, Eol, Equal, False, FloatingPoint, Fn, For,
|
||||
|
|
@ -18,7 +20,6 @@ use crate::value::Value;
|
|||
use crate::{DATE_FORMAT_TIMEZONE, Expr, Stmt, SymbolTable};
|
||||
use log::debug;
|
||||
use std::collections::HashMap;
|
||||
use crate::builtins::globals::GLOBAL_FUNCTIONS;
|
||||
|
||||
pub fn compile(
|
||||
path: Option<&str>,
|
||||
|
|
@ -237,7 +238,9 @@ impl AstCompiler {
|
|||
fn function_declaration(&mut self, symbol_table: &mut SymbolTable) -> Stmt {
|
||||
let name_token = self.consume(&Identifier, Expected("function name."))?;
|
||||
if GLOBAL_FUNCTIONS.contains_key(name_token.lexeme.as_str()) {
|
||||
return Err(self.raise(CompilerError::ReservedFunctionName(name_token.lexeme.clone())))
|
||||
return Err(self.raise(CompilerError::ReservedFunctionName(
|
||||
name_token.lexeme.clone(),
|
||||
)));
|
||||
}
|
||||
self.consume(&LeftParen, Expected("'(' after function name."))?;
|
||||
let mut parameters = vec![];
|
||||
|
|
@ -325,11 +328,7 @@ impl AstCompiler {
|
|||
}
|
||||
|
||||
fn statement(&mut self, symbol_table: &mut SymbolTable) -> Stmt {
|
||||
if self.match_token(&[Print]) {
|
||||
self.print_statement(symbol_table)
|
||||
} else if self.match_token(&[If]) {
|
||||
self.if_statement(symbol_table)
|
||||
} else if self.match_token(&[For]) {
|
||||
if self.match_token(&[For]) {
|
||||
self.for_statement(symbol_table)
|
||||
} else {
|
||||
self.expr_statement(symbol_table)
|
||||
|
|
@ -352,39 +351,10 @@ impl AstCompiler {
|
|||
})
|
||||
}
|
||||
|
||||
fn if_statement(&mut self, symbol_table: &mut SymbolTable) -> Stmt {
|
||||
let condition = self.expression(symbol_table)?;
|
||||
self.consume(&Colon, Expected("':' after if condition."))?;
|
||||
|
||||
self.inc_indent();
|
||||
let then_branch = self.compile(symbol_table)?;
|
||||
|
||||
let else_branch = if self.check(&Else) {
|
||||
self.consume(&Else, Expected("'else' after if condition."))?;
|
||||
self.consume(&Colon, Expected("':' after 'else'."))?;
|
||||
|
||||
self.inc_indent();
|
||||
Some(self.compile(symbol_table)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok(Statement::IfStatement {
|
||||
condition,
|
||||
then_branch,
|
||||
else_branch,
|
||||
})
|
||||
}
|
||||
|
||||
fn inc_indent(&mut self) {
|
||||
self.indent.push(self.indent.last().unwrap() + 1);
|
||||
}
|
||||
|
||||
fn print_statement(&mut self, symbol_table: &mut SymbolTable) -> Stmt {
|
||||
let expr = self.expression(symbol_table)?;
|
||||
self.consume(&Eol, Expected("end of line after print statement."))?;
|
||||
Ok(Statement::PrintStmt { value: expr })
|
||||
}
|
||||
|
||||
fn expr_statement(&mut self, symbol_table: &mut SymbolTable) -> Stmt {
|
||||
let expr = self.expression(symbol_table)?;
|
||||
if !self.is_at_end() {
|
||||
|
|
@ -516,6 +486,33 @@ impl AstCompiler {
|
|||
operator,
|
||||
right: Box::new(right),
|
||||
})
|
||||
} else {
|
||||
self.equals(symbol_table)
|
||||
}
|
||||
}
|
||||
|
||||
fn equals(&mut self, symbol_table: &mut SymbolTable) -> Expr {
|
||||
if self.match_token(&[If]) {
|
||||
let condition = self.expression(symbol_table)?;
|
||||
self.consume(&Colon, Expected("':' after if condition."))?;
|
||||
|
||||
self.inc_indent();
|
||||
let then_branch = self.compile(symbol_table)?;
|
||||
|
||||
let else_branch = if self.check(&Else) {
|
||||
self.consume(&Else, Expected("'else' after if condition."))?;
|
||||
self.consume(&Colon, Expected("':' after 'else'."))?;
|
||||
|
||||
self.inc_indent();
|
||||
Some(self.compile(symbol_table)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok(IfExpression {
|
||||
condition: Box::new(condition),
|
||||
then_branch,
|
||||
else_branch,
|
||||
})
|
||||
} else {
|
||||
self.get(symbol_table)
|
||||
}
|
||||
|
|
@ -865,11 +862,6 @@ pub enum Statement {
|
|||
if_expr: Expression,
|
||||
then_expr: Expression,
|
||||
},
|
||||
IfStatement {
|
||||
condition: Expression,
|
||||
then_branch: Vec<Statement>,
|
||||
else_branch: Option<Vec<Statement>>,
|
||||
},
|
||||
ForStatement {
|
||||
loop_var: Token,
|
||||
range: Expression,
|
||||
|
|
@ -886,7 +878,6 @@ impl Statement {
|
|||
Statement::FunctionStmt { function, .. } => function.name.line,
|
||||
Statement::ObjectStmt { name, .. } => name.line,
|
||||
Statement::GuardStatement { if_expr, .. } => if_expr.line(),
|
||||
Statement::IfStatement { condition, .. } => condition.line(),
|
||||
Statement::ForStatement { loop_var, .. } => loop_var.line,
|
||||
}
|
||||
}
|
||||
|
|
@ -989,6 +980,11 @@ pub enum Expression {
|
|||
receiver: Box<Expression>,
|
||||
field: String,
|
||||
},
|
||||
IfExpression {
|
||||
condition: Box<Expression>,
|
||||
then_branch: Vec<Statement>,
|
||||
else_branch: Option<Vec<Statement>>,
|
||||
},
|
||||
}
|
||||
|
||||
impl Expression {
|
||||
|
|
@ -1010,6 +1006,7 @@ impl Expression {
|
|||
MapGet { .. } => 0,
|
||||
ListGet { .. } => 0,
|
||||
FieldGet { .. } => 0,
|
||||
IfExpression { condition, .. } => condition.line(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::DATE_FORMAT_TIMEZONE;
|
||||
use crate::compiler::{compile, run};
|
||||
use crate::errors::CompilerError::{IllegalArgumentsException, ReservedFunctionName};
|
||||
use crate::errors::CompilerErrorAtLine;
|
||||
|
|
@ -7,7 +8,6 @@ mod tests {
|
|||
use crate::errors::TipiLangError::{Compiler, Runtime};
|
||||
use crate::value::{Value, string};
|
||||
use chrono::DateTime;
|
||||
use crate::DATE_FORMAT_TIMEZONE;
|
||||
|
||||
#[test]
|
||||
fn literal_int() {
|
||||
|
|
@ -241,11 +241,9 @@ m["name"]"#);
|
|||
run(r#"let date:datetime = d"2025-11-09 16:44:28.000 +0100"
|
||||
date"#),
|
||||
Ok(Value::DateTime(Box::new(
|
||||
DateTime::parse_from_str(
|
||||
"2025-11-09 16:44:28.000 +0100", DATE_FORMAT_TIMEZONE
|
||||
)
|
||||
.unwrap()
|
||||
.into()
|
||||
DateTime::parse_from_str("2025-11-09 16:44:28.000 +0100", DATE_FORMAT_TIMEZONE)
|
||||
.unwrap()
|
||||
.into()
|
||||
)))
|
||||
);
|
||||
}
|
||||
|
|
@ -420,9 +418,24 @@ sum
|
|||
#[test]
|
||||
fn global_fns_are_not_allowed() {
|
||||
let value = run(r#"fn now():"#);
|
||||
assert_eq!(value, Err(Compiler(CompilerErrorAtLine { error: ReservedFunctionName("now".to_string()), line: 1 })));
|
||||
assert_eq!(
|
||||
value,
|
||||
Err(Compiler(CompilerErrorAtLine {
|
||||
error: ReservedFunctionName("now".to_string()),
|
||||
line: 1
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
run(r#"
|
||||
let a:i64 = if true:
|
||||
42
|
||||
else:
|
||||
0
|
||||
a"#).unwrap();
|
||||
}
|
||||
// #[test]
|
||||
// fn package() {
|
||||
// assert_eq!(run(r#"a.b.c()"#), Ok(Value::U32(48)));
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ pub fn calculate_type(
|
|||
Ok(if declared_type != &Unknown {
|
||||
if declared_type != inferred_type {
|
||||
match (declared_type, inferred_type) {
|
||||
(I64, Unknown) => I64,
|
||||
(I32, I64) => I32, //need this?
|
||||
(I32, Integer) => I32,
|
||||
(U32, I64) => U32,
|
||||
|
|
@ -210,12 +211,13 @@ pub fn infer_type(expr: &Expression, symbols: &HashMap<String, Symbol>) -> Token
|
|||
infer_type(receiver, symbols)
|
||||
}
|
||||
}
|
||||
Expression::Stop { .. } => TokenType::Unknown,
|
||||
// Expression::PathMatch { .. } => TokenType::Unknown,
|
||||
Expression::NamedParameter { .. } => TokenType::Unknown,
|
||||
Expression::ListGet { .. } => TokenType::Unknown,
|
||||
Expression::MapGet { .. } => TokenType::Unknown,
|
||||
Expression::FieldGet { .. } => TokenType::Unknown,
|
||||
Expression::Stop { .. } => Unknown,
|
||||
// Expression::PathMatch { .. } => Unknown,
|
||||
Expression::NamedParameter { .. } => Unknown,
|
||||
Expression::ListGet { .. } => Unknown,
|
||||
Expression::MapGet { .. } => Unknown,
|
||||
Expression::FieldGet { .. } => Unknown,
|
||||
Expression::Range { lower, .. } => infer_type(lower, symbols),
|
||||
Expression::IfExpression { .. } => Unknown,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue