range loop working
This commit is contained in:
parent
db183e95c4
commit
8c728f6acb
11 changed files with 246 additions and 47 deletions
|
|
@ -1,12 +1,18 @@
|
||||||
use crate::ast_compiler::Expression::{
|
use crate::ast_compiler::Expression::{
|
||||||
FieldGet, FunctionCall, ListGet, MapGet, MethodCall, NamedParameter, Stop, Variable,
|
Assignment, FieldGet, FunctionCall, ListGet, MapGet, MethodCall, NamedParameter, Stop, Variable,
|
||||||
};
|
};
|
||||||
use crate::errors::CompilerError::{
|
use crate::errors::CompilerError::{
|
||||||
self, Expected, ParseError, TooManyParameters, UnexpectedIndent, UninitializedVariable,
|
self, Expected, ParseError, TooManyParameters, UnexpectedIndent, UninitializedVariable,
|
||||||
};
|
};
|
||||||
use crate::errors::CompilerErrorAtLine;
|
use crate::errors::CompilerErrorAtLine;
|
||||||
use crate::symbol_builder::{Symbol, calculate_type, infer_type};
|
use crate::symbol_builder::{Symbol, calculate_type, infer_type};
|
||||||
use crate::tokens::TokenType::{Bang, Bool, Char, Colon, DateTime, Dot, Eof, Eol, Equal, False, FloatingPoint, Fn, Greater, GreaterEqual, GreaterGreater, Identifier, If, Indent, Integer, LeftBrace, LeftBracket, LeftParen, Less, LessEqual, LessLess, Let, ListType, MapType, Minus, Object, Plus, Print, RightBrace, RightBracket, RightParen, SingleRightArrow, Slash, Star, StringType, True, U32, U64, Unknown, Else};
|
use crate::tokens::TokenType::{
|
||||||
|
Bang, Bool, Char, Colon, DateTime, Dot, Else, Eof, Eol, Equal, False, FloatingPoint, Fn, For,
|
||||||
|
Greater, GreaterEqual, GreaterGreater, Identifier, If, In, Indent, Integer, LeftBrace,
|
||||||
|
LeftBracket, LeftParen, Less, LessEqual, LessLess, Let, ListType, MapType, Minus, Object, Plus,
|
||||||
|
Print, Range, RightBrace, RightBracket, RightParen, SingleRightArrow, Slash, Star, StringType,
|
||||||
|
True, U32, U64, Unknown,
|
||||||
|
};
|
||||||
use crate::tokens::{Token, TokenType};
|
use crate::tokens::{Token, TokenType};
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
use crate::{Expr, Stmt, SymbolTable};
|
use crate::{Expr, Stmt, SymbolTable};
|
||||||
|
|
@ -66,6 +72,9 @@ impl AstCompiler {
|
||||||
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() {
|
||||||
|
if self.match_token(&[Eol]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let statement = self.indent(symbol_table)?;
|
let statement = self.indent(symbol_table)?;
|
||||||
if let Some(statement) = statement {
|
if let Some(statement) = statement {
|
||||||
statements.push(statement);
|
statements.push(statement);
|
||||||
|
|
@ -316,11 +325,29 @@ impl AstCompiler {
|
||||||
self.print_statement(symbol_table)
|
self.print_statement(symbol_table)
|
||||||
} else if self.match_token(&[If]) {
|
} else if self.match_token(&[If]) {
|
||||||
self.if_statement(symbol_table)
|
self.if_statement(symbol_table)
|
||||||
|
} else if self.match_token(&[For]) {
|
||||||
|
self.for_statement(symbol_table)
|
||||||
} else {
|
} else {
|
||||||
self.expr_statement(symbol_table)
|
self.expr_statement(symbol_table)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn for_statement(&mut self, symbol_table: &mut SymbolTable) -> Stmt {
|
||||||
|
let loop_var = self.consume(&Identifier, Expected("loop variable name."))?;
|
||||||
|
self.consume(&In, Expected("'in' after loop variable name."))?;
|
||||||
|
let range = self.expression(symbol_table)?;
|
||||||
|
self.consume(&Colon, Expected("colon after range expression"))?;
|
||||||
|
self.consume(&Eol, Expected("end of line after for expression."))?;
|
||||||
|
self.inc_indent();
|
||||||
|
let body = self.compile(symbol_table)?;
|
||||||
|
|
||||||
|
Ok(Statement::ForStatement {
|
||||||
|
loop_var,
|
||||||
|
range,
|
||||||
|
body,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn if_statement(&mut self, symbol_table: &mut SymbolTable) -> Stmt {
|
fn if_statement(&mut self, symbol_table: &mut SymbolTable) -> Stmt {
|
||||||
let condition = self.expression(symbol_table)?;
|
let condition = self.expression(symbol_table)?;
|
||||||
self.consume(&Colon, Expected("':' after if condition."))?;
|
self.consume(&Colon, Expected("':' after if condition."))?;
|
||||||
|
|
@ -337,7 +364,11 @@ impl AstCompiler {
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
Ok(Statement::IfStatement {condition, then_branch, else_branch})
|
Ok(Statement::IfStatement {
|
||||||
|
condition,
|
||||||
|
then_branch,
|
||||||
|
else_branch,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn inc_indent(&mut self) {
|
fn inc_indent(&mut self) {
|
||||||
|
|
@ -389,7 +420,21 @@ impl AstCompiler {
|
||||||
|
|
||||||
fn assignment(&mut self, symbol_table: &mut SymbolTable) -> Expr {
|
fn assignment(&mut self, symbol_table: &mut SymbolTable) -> Expr {
|
||||||
let expr = self.equality(symbol_table)?;
|
let expr = self.equality(symbol_table)?;
|
||||||
self.binary(&[TokenType::Equal], expr, symbol_table)
|
if self.match_token(&[Equal]) {
|
||||||
|
let operator = self.previous().clone();
|
||||||
|
let right = self.comparison(symbol_table)?;
|
||||||
|
if let Variable { name, .. } = expr {
|
||||||
|
Ok(Assignment {
|
||||||
|
line: operator.line,
|
||||||
|
variable_name: name.to_string(),
|
||||||
|
value: Box::new(right),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(self.raise(CompilerError::Failure))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Ok(expr)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn equality(&mut self, symbol_table: &mut SymbolTable) -> Expr {
|
fn equality(&mut self, symbol_table: &mut SymbolTable) -> Expr {
|
||||||
|
|
@ -402,7 +447,7 @@ impl AstCompiler {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn comparison(&mut self, symbol_table: &mut SymbolTable) -> Expr {
|
fn comparison(&mut self, symbol_table: &mut SymbolTable) -> Expr {
|
||||||
let expr = self.bitshift(symbol_table)?;
|
let expr = self.range(symbol_table)?;
|
||||||
self.binary(
|
self.binary(
|
||||||
&[Greater, GreaterEqual, Less, LessEqual],
|
&[Greater, GreaterEqual, Less, LessEqual],
|
||||||
expr,
|
expr,
|
||||||
|
|
@ -410,6 +455,20 @@ impl AstCompiler {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn range(&mut self, symbol_table: &mut SymbolTable) -> Expr {
|
||||||
|
let mut expr = self.bitshift(symbol_table)?;
|
||||||
|
if self.match_token(&[Range]) {
|
||||||
|
let operator = self.previous().clone();
|
||||||
|
let right = self.expression(symbol_table)?;
|
||||||
|
expr = Expression::Range {
|
||||||
|
line: operator.line,
|
||||||
|
lower: Box::new(expr),
|
||||||
|
upper: Box::new(right),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Ok(expr)
|
||||||
|
}
|
||||||
|
|
||||||
fn bitshift(&mut self, symbol_table: &mut SymbolTable) -> Expr {
|
fn bitshift(&mut self, symbol_table: &mut SymbolTable) -> Expr {
|
||||||
let expr = self.term(symbol_table)?;
|
let expr = self.term(symbol_table)?;
|
||||||
self.binary(&[GreaterGreater, LessLess], expr, symbol_table)
|
self.binary(&[GreaterGreater, LessLess], expr, symbol_table)
|
||||||
|
|
@ -805,11 +864,16 @@ pub enum Statement {
|
||||||
if_expr: Expression,
|
if_expr: Expression,
|
||||||
then_expr: Expression,
|
then_expr: Expression,
|
||||||
},
|
},
|
||||||
IfStatement{
|
IfStatement {
|
||||||
condition: Expression,
|
condition: Expression,
|
||||||
then_branch: Vec<Statement>,
|
then_branch: Vec<Statement>,
|
||||||
else_branch: Option<Vec<Statement>>
|
else_branch: Option<Vec<Statement>>,
|
||||||
}
|
},
|
||||||
|
ForStatement {
|
||||||
|
loop_var: Token,
|
||||||
|
range: Expression,
|
||||||
|
body: Vec<Statement>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Statement {
|
impl Statement {
|
||||||
|
|
@ -822,6 +886,7 @@ impl Statement {
|
||||||
Statement::ObjectStmt { name, .. } => name.line,
|
Statement::ObjectStmt { name, .. } => name.line,
|
||||||
Statement::GuardStatement { if_expr, .. } => if_expr.line(),
|
Statement::GuardStatement { if_expr, .. } => if_expr.line(),
|
||||||
Statement::IfStatement { condition, .. } => condition.line(),
|
Statement::IfStatement { condition, .. } => condition.line(),
|
||||||
|
Statement::ForStatement { loop_var, .. } => loop_var.line,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -867,6 +932,11 @@ pub enum Expression {
|
||||||
literaltype: TokenType,
|
literaltype: TokenType,
|
||||||
value: Value,
|
value: Value,
|
||||||
},
|
},
|
||||||
|
Range {
|
||||||
|
line: usize,
|
||||||
|
lower: Box<Expression>,
|
||||||
|
upper: Box<Expression>,
|
||||||
|
},
|
||||||
List {
|
List {
|
||||||
line: usize,
|
line: usize,
|
||||||
literaltype: TokenType,
|
literaltype: TokenType,
|
||||||
|
|
@ -882,6 +952,11 @@ pub enum Expression {
|
||||||
name: String,
|
name: String,
|
||||||
var_type: TokenType,
|
var_type: TokenType,
|
||||||
},
|
},
|
||||||
|
Assignment {
|
||||||
|
line: usize,
|
||||||
|
variable_name: String,
|
||||||
|
value: Box<Expression>,
|
||||||
|
},
|
||||||
FunctionCall {
|
FunctionCall {
|
||||||
line: usize,
|
line: usize,
|
||||||
name: String,
|
name: String,
|
||||||
|
|
@ -922,9 +997,11 @@ impl Expression {
|
||||||
Self::Unary { line, .. } => *line,
|
Self::Unary { line, .. } => *line,
|
||||||
Self::Grouping { line, .. } => *line,
|
Self::Grouping { line, .. } => *line,
|
||||||
Self::Literal { line, .. } => *line,
|
Self::Literal { line, .. } => *line,
|
||||||
|
Self::Range { line, .. } => *line,
|
||||||
Self::List { line, .. } => *line,
|
Self::List { line, .. } => *line,
|
||||||
Self::Map { line, .. } => *line,
|
Self::Map { line, .. } => *line,
|
||||||
Variable { line, .. } => *line,
|
Variable { line, .. } => *line,
|
||||||
|
Assignment { line, .. } => *line,
|
||||||
FunctionCall { line, .. } => *line,
|
FunctionCall { line, .. } => *line,
|
||||||
MethodCall { line, .. } => *line,
|
MethodCall { line, .. } => *line,
|
||||||
Stop { line } => *line,
|
Stop { line } => *line,
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,10 @@ use crate::errors::{CompilerError, CompilerErrorAtLine};
|
||||||
use crate::symbol_builder::{Symbol, calculate_type, infer_type};
|
use crate::symbol_builder::{Symbol, calculate_type, infer_type};
|
||||||
use crate::tokens::TokenType;
|
use crate::tokens::TokenType;
|
||||||
use crate::tokens::TokenType::Unknown;
|
use crate::tokens::TokenType::Unknown;
|
||||||
use crate::value::{Value};
|
use crate::value::Value;
|
||||||
use crate::vm::{
|
use crate::vm::{
|
||||||
OP_ADD, OP_AND, OP_ASSIGN, OP_BITAND, OP_BITOR, OP_BITXOR, OP_CALL, OP_CALL_BUILTIN,
|
OP_ADD, OP_AND, OP_ASSIGN, OP_BITAND, OP_BITOR, OP_BITXOR, OP_CALL, OP_CALL_BUILTIN,
|
||||||
OP_CONSTANT, OP_DEF_LIST, OP_DEF_MAP, OP_DIVIDE, OP_EQUAL, OP_GET, OP_GREATER,
|
OP_CONSTANT, OP_DEF_LIST, OP_DEF_MAP, OP_DIVIDE, OP_EQUAL, OP_GET, OP_GOTO_IF, OP_GREATER,
|
||||||
OP_GREATER_EQUAL, OP_IF, OP_IF_ELSE, OP_LESS, OP_LESS_EQUAL, OP_LIST_GET, OP_MULTIPLY,
|
OP_GREATER_EQUAL, OP_IF, OP_IF_ELSE, OP_LESS, OP_LESS_EQUAL, OP_LIST_GET, OP_MULTIPLY,
|
||||||
OP_NEGATE, OP_NOT, OP_OR, OP_POP, OP_PRINT, OP_RETURN, OP_SHL, OP_SHR, OP_SUBTRACT,
|
OP_NEGATE, OP_NOT, OP_OR, OP_POP, OP_PRINT, OP_RETURN, OP_SHL, OP_SHR, OP_SUBTRACT,
|
||||||
};
|
};
|
||||||
|
|
@ -76,6 +76,7 @@ impl Compiler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// compile the entire AST into a chunk, adding a RETURN OP
|
||||||
pub(crate) fn compile(
|
pub(crate) fn compile(
|
||||||
&mut self,
|
&mut self,
|
||||||
ast: &Vec<Statement>,
|
ast: &Vec<Statement>,
|
||||||
|
|
@ -83,20 +84,28 @@ impl Compiler {
|
||||||
registry: &mut Registry,
|
registry: &mut Registry,
|
||||||
namespace: &str,
|
namespace: &str,
|
||||||
) -> Result<Chunk, CompilerErrorAtLine> {
|
) -> Result<Chunk, CompilerErrorAtLine> {
|
||||||
for statement in ast {
|
self.compile_statements(ast, symbols, registry, namespace)?;
|
||||||
self.compile_statement(statement, symbols, registry, namespace)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.emit_byte(OP_RETURN);
|
self.emit_byte(OP_RETURN);
|
||||||
let chunk = self.chunk.clone();
|
let chunk = self.chunk.clone();
|
||||||
self.chunk.code.clear(); // in case the compiler is reused, clear it for the next compilation. This is for the REPL
|
self.chunk.code.clear(); // in case the compiler is reused, clear it for the next compilation. This is for the REPL
|
||||||
Ok(chunk)
|
Ok(chunk)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn raise(&self, error: CompilerError) -> CompilerErrorAtLine {
|
/// compile the entire AST into a chunk
|
||||||
CompilerErrorAtLine::raise(error, self.current_line)
|
fn compile_statements(
|
||||||
|
&mut self,
|
||||||
|
ast: &Vec<Statement>,
|
||||||
|
symbols: &SymbolTable,
|
||||||
|
registry: &mut Registry,
|
||||||
|
namespace: &str,
|
||||||
|
) -> Result<(), CompilerErrorAtLine> {
|
||||||
|
for statement in ast {
|
||||||
|
self.compile_statement(statement, symbols, registry, namespace)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// compile a single statement
|
||||||
fn compile_statement(
|
fn compile_statement(
|
||||||
&mut self,
|
&mut self,
|
||||||
statement: &Statement,
|
statement: &Statement,
|
||||||
|
|
@ -174,6 +183,38 @@ impl Compiler {
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Statement::ForStatement {
|
||||||
|
loop_var,
|
||||||
|
range,
|
||||||
|
body,
|
||||||
|
} => {
|
||||||
|
// 1. step var index
|
||||||
|
let step_const_index = self.emit_constant(Value::I64(1));
|
||||||
|
// 2. range expression
|
||||||
|
self.compile_expression(namespace, range, symbols, registry)?;
|
||||||
|
//save the constants for lower and upper bounds of the range
|
||||||
|
let start_index = self.chunk.constants.len() - 1;
|
||||||
|
let end_index = self.chunk.constants.len() - 2;
|
||||||
|
|
||||||
|
let name = loop_var.lexeme.as_str();
|
||||||
|
let loop_var_name_index = self.chunk.add_var(&loop_var.token_type, name);
|
||||||
|
self.vars.insert(name.to_string(), loop_var_name_index);
|
||||||
|
|
||||||
|
// 3. start index
|
||||||
|
self.emit_bytes(OP_CONSTANT, start_index as u16);
|
||||||
|
self.emit_bytes(OP_ASSIGN, loop_var_name_index as u16);
|
||||||
|
|
||||||
|
let return_addr = self.chunk.code.len();
|
||||||
|
self.compile_statements(body, symbols, registry, namespace)?;
|
||||||
|
self.emit_bytes(OP_GET, loop_var_name_index as u16);
|
||||||
|
self.emit_bytes(OP_CONSTANT, step_const_index);
|
||||||
|
self.emit_byte(OP_ADD);
|
||||||
|
self.emit_bytes(OP_ASSIGN, loop_var_name_index as u16);
|
||||||
|
self.emit_bytes(OP_CONSTANT, end_index as u16);
|
||||||
|
self.emit_bytes(OP_GET, loop_var_name_index as u16);
|
||||||
|
self.emit_byte(OP_GREATER_EQUAL);
|
||||||
|
self.emit_bytes(OP_GOTO_IF, return_addr as u16);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -262,6 +303,15 @@ impl Compiler {
|
||||||
return Err(self.raise(UndeclaredVariable(name.to_string())));
|
return Err(self.raise(UndeclaredVariable(name.to_string())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Expression::Assignment { variable_name, value, .. } => {
|
||||||
|
self.compile_expression(namespace, value, symbols, registry)?;
|
||||||
|
let name_index = self.vars.get(variable_name);
|
||||||
|
if let Some(name_index) = name_index {
|
||||||
|
self.emit_bytes(OP_ASSIGN, *name_index as u16);
|
||||||
|
} else {
|
||||||
|
return Err(self.raise(UndeclaredVariable(variable_name.to_string())));
|
||||||
|
}
|
||||||
|
}
|
||||||
Expression::Literal { value, .. } => {
|
Expression::Literal { value, .. } => {
|
||||||
self.emit_constant(value.clone());
|
self.emit_constant(value.clone());
|
||||||
}
|
}
|
||||||
|
|
@ -343,6 +393,11 @@ impl Compiler {
|
||||||
}
|
}
|
||||||
Expression::MapGet { .. } => {}
|
Expression::MapGet { .. } => {}
|
||||||
Expression::FieldGet { .. } => {}
|
Expression::FieldGet { .. } => {}
|
||||||
|
Expression::Range { lower, upper, .. } => {
|
||||||
|
// opposite order, because we have to assign last one first to the loop variable
|
||||||
|
self.compile_expression(namespace, upper, symbols, registry)?;
|
||||||
|
self.compile_expression(namespace, lower, symbols, registry)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -393,4 +448,8 @@ impl Compiler {
|
||||||
self.emit_bytes(OP_CONSTANT, index);
|
self.emit_bytes(OP_CONSTANT, index);
|
||||||
index
|
index
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn raise(&self, error: CompilerError) -> CompilerErrorAtLine {
|
||||||
|
CompilerErrorAtLine::raise(error, self.current_line)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
13
src/chunk.rs
13
src/chunk.rs
|
|
@ -5,7 +5,7 @@ use crate::vm::{
|
||||||
OP_ADD, OP_BITAND, OP_BITOR, OP_BITXOR, OP_CALL, OP_CONSTANT, OP_DEF_BOOL, OP_DEF_F32,
|
OP_ADD, OP_BITAND, OP_BITOR, OP_BITXOR, OP_CALL, OP_CONSTANT, OP_DEF_BOOL, OP_DEF_F32,
|
||||||
OP_DEF_F64, OP_DEF_I32, OP_DEF_I64, OP_DEF_LIST, OP_DEF_MAP, OP_DEF_STRING, OP_DEFINE,
|
OP_DEF_F64, OP_DEF_I32, OP_DEF_I64, OP_DEF_LIST, OP_DEF_MAP, OP_DEF_STRING, OP_DEFINE,
|
||||||
OP_DIVIDE, OP_EQUAL, OP_GET, OP_GREATER, OP_GREATER_EQUAL, OP_LESS, OP_LESS_EQUAL, OP_MULTIPLY,
|
OP_DIVIDE, OP_EQUAL, OP_GET, OP_GREATER, OP_GREATER_EQUAL, OP_LESS, OP_LESS_EQUAL, OP_MULTIPLY,
|
||||||
OP_NEGATE, OP_NOT, OP_POP, OP_PRINT, OP_RETURN, OP_SHL, OP_SHR, OP_SUBTRACT,
|
OP_NEGATE, OP_NOT, OP_POP, OP_PRINT, OP_RETURN, OP_SHL, OP_SHR, OP_SUBTRACT,OP_ASSIGN,OP_GOTO_IF
|
||||||
};
|
};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
|
@ -117,9 +117,11 @@ impl Chunk {
|
||||||
OP_DEF_F64 => self.constant_inst("DEFF64", offset),
|
OP_DEF_F64 => self.constant_inst("DEFF64", offset),
|
||||||
OP_DEF_BOOL => self.constant_inst("DEFBOOL", offset),
|
OP_DEF_BOOL => self.constant_inst("DEFBOOL", offset),
|
||||||
OP_CALL => self.call_inst("CALL", offset),
|
OP_CALL => self.call_inst("CALL", offset),
|
||||||
OP_GET => self.constant_inst("GET", offset),
|
OP_GET => self.assign_inst("GET", offset),
|
||||||
OP_DEF_LIST => self.new_inst("DEFLIST", offset),
|
OP_DEF_LIST => self.new_inst("DEFLIST", offset),
|
||||||
OP_DEF_MAP => self.new_inst("DEFMAP", offset),
|
OP_DEF_MAP => self.new_inst("DEFMAP", offset),
|
||||||
|
OP_ASSIGN => self.assign_inst("ASSIGN", offset),
|
||||||
|
OP_GOTO_IF => self.constant_inst("GOTO_IF", offset),
|
||||||
_ => {
|
_ => {
|
||||||
println!("Unknown instruction {}", instruction);
|
println!("Unknown instruction {}", instruction);
|
||||||
offset + 1
|
offset + 1
|
||||||
|
|
@ -132,6 +134,13 @@ impl Chunk {
|
||||||
offset + 1
|
offset + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn assign_inst(&self, op: &str, offset: usize) -> usize {
|
||||||
|
let constant = self.code[offset + 1];
|
||||||
|
print!("{} {}:", op, constant);
|
||||||
|
println!("{}",self.vars.get(constant as usize).unwrap().1);
|
||||||
|
offset + 2
|
||||||
|
}
|
||||||
|
|
||||||
fn call_inst(&self, op: &str, offset: usize) -> usize {
|
fn call_inst(&self, op: &str, offset: usize) -> usize {
|
||||||
let constant = self.code[offset + 1];
|
let constant = self.code[offset + 1];
|
||||||
let num_args = self.code[offset + 2];
|
let num_args = self.code[offset + 2];
|
||||||
|
|
|
||||||
|
|
@ -156,7 +156,7 @@ object Person:
|
||||||
|
|
||||||
let p = Person(name: 0x42)
|
let p = Person(name: 0x42)
|
||||||
p"#);
|
p"#);
|
||||||
assert!(r.is_err());
|
// assert!(r.is_err());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
r#"Compilation failed: error at line 5, Expected string, found integer"#,
|
r#"Compilation failed: error at line 5, Expected string, found integer"#,
|
||||||
format!("{}", r.unwrap_err().to_string())
|
format!("{}", r.unwrap_err().to_string())
|
||||||
|
|
@ -386,6 +386,24 @@ else:
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn inline_comment(){
|
||||||
|
assert_eq!(run(r#"// this is a comment"#), Ok(Value::Void));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn range_loop() {
|
||||||
|
assert_eq!(
|
||||||
|
run(r#"
|
||||||
|
let sum=0
|
||||||
|
for a in 1..4:
|
||||||
|
sum = sum + a
|
||||||
|
sum
|
||||||
|
"#),
|
||||||
|
Ok(Value::I64(10))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// #[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)));
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ pub(crate) fn get_keyword(lexeme: &str) -> Option<TokenType> {
|
||||||
"fn" => Some(TokenType::Fn),
|
"fn" => Some(TokenType::Fn),
|
||||||
"for" => Some(TokenType::For),
|
"for" => Some(TokenType::For),
|
||||||
"if" => Some(TokenType::If),
|
"if" => Some(TokenType::If),
|
||||||
|
"in" => Some(TokenType::In),
|
||||||
"i32" => Some(TokenType::I32),
|
"i32" => Some(TokenType::I32),
|
||||||
"i64" => Some(TokenType::I64),
|
"i64" => Some(TokenType::I64),
|
||||||
"let" => Some(TokenType::Let),
|
"let" => Some(TokenType::Let),
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ use crate::symbol_builder::Symbol;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
use crate::value::Value::Void;
|
||||||
|
|
||||||
pub mod ast_compiler;
|
pub mod ast_compiler;
|
||||||
mod builtins;
|
mod builtins;
|
||||||
|
|
@ -92,6 +93,7 @@ pub(crate) fn run(src: &str) -> Result<value::Value, CrudLangError> {
|
||||||
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)?;
|
||||||
|
|
||||||
let registry = arc_swap::ArcSwap::from(std::sync::Arc::new(registry));
|
let registry = arc_swap::ArcSwap::from(std::sync::Arc::new(registry));
|
||||||
vm::interpret(registry.load(), "main").map_err(CrudLangError::from)
|
vm::interpret(registry.load(), "main").map_err(CrudLangError::from)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ use crate::chunk::Chunk;
|
||||||
use crate::errors::CrudLangError;
|
use crate::errors::CrudLangError;
|
||||||
use crate::scanner::scan;
|
use crate::scanner::scan;
|
||||||
use crate::vm::Vm;
|
use crate::vm::Vm;
|
||||||
use crate::{ast_compiler, bytecode_compiler, map_underlying, recompile, symbol_builder};
|
use crate::{ast_compiler, bytecode_compiler, map_underlying, symbol_builder};
|
||||||
use arc_swap::ArcSwap;
|
use arc_swap::ArcSwap;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
|
||||||
|
|
@ -142,7 +142,7 @@ impl Scanner {
|
||||||
if c == '0' && self.peek() == 'x' {
|
if c == '0' && self.peek() == 'x' {
|
||||||
self.hex_number()?;
|
self.hex_number()?;
|
||||||
} else if c.is_ascii_digit() {
|
} else if c.is_ascii_digit() {
|
||||||
self.number();
|
self.number_or_range();
|
||||||
} else if is_alpha(c) {
|
} else if is_alpha(c) {
|
||||||
self.identifier();
|
self.identifier();
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -181,7 +181,7 @@ impl Scanner {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn number(&mut self) {
|
fn number_or_range(&mut self) {
|
||||||
while self.peek().is_ascii_digit() {
|
while self.peek().is_ascii_digit() {
|
||||||
self.advance();
|
self.advance();
|
||||||
}
|
}
|
||||||
|
|
@ -190,13 +190,30 @@ impl Scanner {
|
||||||
has_dot = true;
|
has_dot = true;
|
||||||
self.advance();
|
self.advance();
|
||||||
}
|
}
|
||||||
|
if self.peek() == '.' && self.peek_next() == '.' {
|
||||||
|
self.range_expression()
|
||||||
|
} else {
|
||||||
while is_digit_or_scientific(self.peek()) {
|
while is_digit_or_scientific(self.peek()) {
|
||||||
self.advance();
|
self.advance();
|
||||||
}
|
}
|
||||||
let value: String = self.chars[self.start..self.current].iter().collect();
|
let value: String = self.chars[self.start..self.current].iter().collect();
|
||||||
self.add_token_with_value(if has_dot { FloatingPoint } else { Integer }, value);
|
self.add_token_with_value(if has_dot { FloatingPoint } else { Integer }, value);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn range_expression(&mut self) {
|
||||||
|
let lower: String = self.chars[self.start..self.current].iter().collect();
|
||||||
|
self.match_next('.');
|
||||||
|
self.match_next('.');
|
||||||
|
self.add_token_with_value(Integer, lower);
|
||||||
|
self.add_token(TokenType::Range);
|
||||||
|
self.start = self.current;
|
||||||
|
while self.peek().is_ascii_digit() {
|
||||||
|
self.advance();
|
||||||
|
}
|
||||||
|
let upper: String = self.chars[self.start..self.current].iter().collect();
|
||||||
|
self.add_token_with_value(Integer, upper);
|
||||||
|
}
|
||||||
|
|
||||||
fn char(&mut self) -> Result<(), CompilerErrorAtLine> {
|
fn char(&mut self) -> Result<(), CompilerErrorAtLine> {
|
||||||
while self.peek() != '\'' && !self.is_at_end() {
|
while self.peek() != '\'' && !self.is_at_end() {
|
||||||
|
|
@ -265,11 +282,15 @@ impl Scanner {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn peek_next(&self) -> char {
|
fn peek_next(&self) -> char {
|
||||||
|
if self.current + 1 >= self.chars.len() {
|
||||||
|
'\0'
|
||||||
|
} else {
|
||||||
self.chars[self.current + 1]
|
self.chars[self.current + 1]
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn match_next(&mut self, expected: char) -> bool {
|
fn match_next(&mut self, expected: char) -> bool {
|
||||||
if self.is_at_end() || self.chars[self.current] != expected{
|
if self.is_at_end() || self.chars[self.current] != expected {
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
self.current += 1;
|
self.current += 1;
|
||||||
|
|
@ -304,7 +325,6 @@ struct Scanner {
|
||||||
new_line: bool,
|
new_line: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn is_digit_or_scientific(c: char) -> bool {
|
fn is_digit_or_scientific(c: char) -> bool {
|
||||||
c.is_ascii_digit() || c == 'e' || c == 'E'
|
c.is_ascii_digit() || c == 'e' || c == 'E'
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::ast_compiler::{Expression, Parameter, Statement};
|
use crate::ast_compiler::{Expression, Parameter, Statement};
|
||||||
use crate::builtins::{Signature, lookup};
|
use crate::builtins::lookup;
|
||||||
use crate::errors::CompilerError;
|
use crate::errors::CompilerError;
|
||||||
use crate::errors::CompilerError::IncompatibleTypes;
|
use crate::errors::CompilerError::IncompatibleTypes;
|
||||||
use crate::tokens::TokenType::{
|
use crate::tokens::TokenType::{
|
||||||
|
|
@ -186,6 +186,7 @@ pub fn infer_type(expr: &Expression, symbols: &HashMap<String, Symbol>) -> Token
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expression::Variable { var_type, .. } => var_type.clone(),
|
Expression::Variable { var_type, .. } => var_type.clone(),
|
||||||
|
Expression::Assignment { value, .. } => infer_type(value, symbols),
|
||||||
Expression::FunctionCall { name, .. } => {
|
Expression::FunctionCall { name, .. } => {
|
||||||
let symbol = symbols.get(name);
|
let symbol = symbols.get(name);
|
||||||
match symbol {
|
match symbol {
|
||||||
|
|
@ -203,7 +204,7 @@ pub fn infer_type(expr: &Expression, symbols: &HashMap<String, Symbol>) -> Token
|
||||||
if let Ok(signature) = lookup(&value.to_string(), method_name) {
|
if let Ok(signature) = lookup(&value.to_string(), method_name) {
|
||||||
signature.return_type.clone()
|
signature.return_type.clone()
|
||||||
} else {
|
} else {
|
||||||
unreachable!()//?
|
unreachable!() //?
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
infer_type(receiver, symbols)
|
infer_type(receiver, symbols)
|
||||||
|
|
@ -215,5 +216,6 @@ pub fn infer_type(expr: &Expression, symbols: &HashMap<String, Symbol>) -> Token
|
||||||
Expression::ListGet { .. } => TokenType::Unknown,
|
Expression::ListGet { .. } => TokenType::Unknown,
|
||||||
Expression::MapGet { .. } => TokenType::Unknown,
|
Expression::MapGet { .. } => TokenType::Unknown,
|
||||||
Expression::FieldGet { .. } => TokenType::Unknown,
|
Expression::FieldGet { .. } => TokenType::Unknown,
|
||||||
|
Expression::Range { lower, .. } => infer_type(lower, symbols),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,7 @@ pub enum TokenType {
|
||||||
I64,
|
I64,
|
||||||
Identifier,
|
Identifier,
|
||||||
If,
|
If,
|
||||||
|
In,
|
||||||
Indent,
|
Indent,
|
||||||
Integer,
|
Integer,
|
||||||
SignedInteger,
|
SignedInteger,
|
||||||
|
|
@ -73,6 +74,7 @@ pub enum TokenType {
|
||||||
Plus,
|
Plus,
|
||||||
Print,
|
Print,
|
||||||
Question,
|
Question,
|
||||||
|
Range,
|
||||||
Return,
|
Return,
|
||||||
RightParen,
|
RightParen,
|
||||||
RightBrace,
|
RightBrace,
|
||||||
|
|
@ -132,6 +134,7 @@ impl fmt::Display for TokenType {
|
||||||
TokenType::Hex => write!(f, "0x"),
|
TokenType::Hex => write!(f, "0x"),
|
||||||
TokenType::If => write!(f, "if"),
|
TokenType::If => write!(f, "if"),
|
||||||
TokenType::Identifier => write!(f, "identifier"),
|
TokenType::Identifier => write!(f, "identifier"),
|
||||||
|
TokenType::In => write!(f, "in"),
|
||||||
TokenType::Indent => write!(f, "indent"),
|
TokenType::Indent => write!(f, "indent"),
|
||||||
TokenType::Integer => write!(f, "integer"),
|
TokenType::Integer => write!(f, "integer"),
|
||||||
TokenType::LeftBrace => write!(f, "{{"),
|
TokenType::LeftBrace => write!(f, "{{"),
|
||||||
|
|
@ -150,6 +153,7 @@ impl fmt::Display for TokenType {
|
||||||
TokenType::Plus => write!(f, "+"),
|
TokenType::Plus => write!(f, "+"),
|
||||||
TokenType::Print => write!(f, "print"),
|
TokenType::Print => write!(f, "print"),
|
||||||
TokenType::Question => write!(f, "?"),
|
TokenType::Question => write!(f, "?"),
|
||||||
|
TokenType::Range => 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, "}}"),
|
||||||
|
|
|
||||||
35
src/vm.rs
35
src/vm.rs
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::Registry;
|
||||||
use crate::chunk::Chunk;
|
use crate::chunk::Chunk;
|
||||||
use crate::errors::RuntimeError::Something;
|
use crate::errors::RuntimeError::Something;
|
||||||
use crate::errors::{RuntimeError, ValueError};
|
use crate::errors::{RuntimeError, ValueError};
|
||||||
|
|
@ -7,7 +8,6 @@ use arc_swap::Guard;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
use crate::Registry;
|
|
||||||
|
|
||||||
pub struct Vm {
|
pub struct Vm {
|
||||||
ip: usize,
|
ip: usize,
|
||||||
|
|
@ -17,14 +17,15 @@ pub struct Vm {
|
||||||
pub(crate) registry: Arc<HashMap<String, Chunk>>,
|
pub(crate) registry: Arc<HashMap<String, Chunk>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn interpret(
|
pub fn interpret(
|
||||||
registry: Guard<Arc<HashMap<String, Chunk>>>,
|
registry: Guard<Arc<HashMap<String, Chunk>>>,
|
||||||
function: &str,
|
function: &str,
|
||||||
) -> Result<Value, RuntimeError> {
|
) -> Result<Value, RuntimeError> {
|
||||||
let chunk = registry.get(function).unwrap().clone();
|
let chunk = registry.get(function).unwrap().clone();
|
||||||
|
// chunk.disassemble();
|
||||||
let mut vm = Vm::new(®istry);
|
let mut vm = Vm::new(®istry);
|
||||||
vm.run(&get_context(function), &chunk)
|
vm.run(&get_context(function), &chunk)
|
||||||
|
// Ok(Value::Void)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn interpret_async(
|
pub async fn interpret_async(
|
||||||
|
|
@ -57,7 +58,7 @@ fn value_map(strings: HashMap<String, String>) -> HashMap<Value, Value> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn interpret_function(chunk: &Chunk, args: Vec<Value>) -> Result<Value, RuntimeError> {
|
pub fn interpret_function(chunk: &Chunk, args: Vec<Value>) -> Result<Value, RuntimeError> {
|
||||||
let mut vm = Vm::new(& Arc::new(HashMap::new()));
|
let mut vm = Vm::new(&Arc::new(HashMap::new()));
|
||||||
vm.run_function(chunk, args)
|
vm.run_function(chunk, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -157,7 +158,7 @@ impl Vm {
|
||||||
let (var_type, name) = chunk.vars.get(index).unwrap();
|
let (var_type, name) = chunk.vars.get(index).unwrap();
|
||||||
let value = self.pop();
|
let value = self.pop();
|
||||||
let value = Self::number(var_type, value)?;
|
let value = Self::number(var_type, value)?;
|
||||||
self.local_vars.insert(name.to_string(), value);
|
self.local_vars.insert(name.to_string(), value); //insert or update
|
||||||
}
|
}
|
||||||
OP_DEF_MAP => {
|
OP_DEF_MAP => {
|
||||||
let len = self.read(chunk);
|
let len = self.read(chunk);
|
||||||
|
|
@ -172,7 +173,7 @@ impl Vm {
|
||||||
OP_GET => {
|
OP_GET => {
|
||||||
let var_index = self.read(chunk);
|
let var_index = self.read(chunk);
|
||||||
let (_, name_index) = chunk.vars.get(var_index).unwrap();
|
let (_, name_index) = chunk.vars.get(var_index).unwrap();
|
||||||
let value = self.local_vars.remove(name_index).unwrap();
|
let value = self.local_vars.get(name_index).unwrap().clone();
|
||||||
self.push(value);
|
self.push(value);
|
||||||
}
|
}
|
||||||
OP_LIST_GET => {
|
OP_LIST_GET => {
|
||||||
|
|
@ -196,10 +197,11 @@ impl Vm {
|
||||||
}
|
}
|
||||||
args.reverse();
|
args.reverse();
|
||||||
let receiver = self.pop();
|
let receiver = self.pop();
|
||||||
let return_value = crate::builtins::call(&receiver_type_name, &function_name, receiver, args)?;
|
let return_value =
|
||||||
|
crate::builtins::call(&receiver_type_name, &function_name, receiver, args)?;
|
||||||
self.push(return_value);
|
self.push(return_value);
|
||||||
}
|
}
|
||||||
OP_POP =>{
|
OP_POP => {
|
||||||
self.pop(); // discards the value
|
self.pop(); // discards the value
|
||||||
}
|
}
|
||||||
OP_CALL => {
|
OP_CALL => {
|
||||||
|
|
@ -232,10 +234,7 @@ impl Vm {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut fields = vec![];
|
let mut fields = vec![];
|
||||||
params
|
params.iter().zip(args).for_each(|(param, arg)| {
|
||||||
.iter()
|
|
||||||
.zip(args)
|
|
||||||
.for_each(|(param, arg)| {
|
|
||||||
fields.push((param.name.lexeme.clone(), arg))
|
fields.push((param.name.lexeme.clone(), arg))
|
||||||
});
|
});
|
||||||
let new_instance = Value::ObjectType(Box::new(Object {
|
let new_instance = Value::ObjectType(Box::new(Object {
|
||||||
|
|
@ -254,7 +253,7 @@ impl Vm {
|
||||||
OP_IF => {
|
OP_IF => {
|
||||||
let condition = self.pop();
|
let condition = self.pop();
|
||||||
if condition == Value::Bool(true) {
|
if condition == Value::Bool(true) {
|
||||||
if let Some(then) = self.registry.get(&format!("{}.?",chunk.name)){
|
if let Some(then) = self.registry.get(&format!("{}.?", chunk.name)) {
|
||||||
let result = interpret_function(then, vec![])?;
|
let result = interpret_function(then, vec![])?;
|
||||||
self.push(result);
|
self.push(result);
|
||||||
}
|
}
|
||||||
|
|
@ -263,19 +262,26 @@ impl Vm {
|
||||||
OP_IF_ELSE => {
|
OP_IF_ELSE => {
|
||||||
let condition = self.pop();
|
let condition = self.pop();
|
||||||
self.push(if condition == Value::Bool(true) {
|
self.push(if condition == Value::Bool(true) {
|
||||||
if let Some(then) = self.registry.get(&format!("{}.?",chunk.name)){
|
if let Some(then) = self.registry.get(&format!("{}.?", chunk.name)) {
|
||||||
interpret_function(then, vec![])?
|
interpret_function(then, vec![])?
|
||||||
} else {
|
} else {
|
||||||
return Err(Something);
|
return Err(Something);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if let Some(then) = self.registry.get(&format!("{}.:",chunk.name)){
|
if let Some(then) = self.registry.get(&format!("{}.:", chunk.name)) {
|
||||||
interpret_function(then, vec![])?
|
interpret_function(then, vec![])?
|
||||||
} else {
|
} else {
|
||||||
return Err(Something);
|
return Err(Something);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
OP_GOTO_IF => {
|
||||||
|
let b = self.pop();
|
||||||
|
let goto_addr = self.read(chunk);
|
||||||
|
if b == Value::Bool(true) {
|
||||||
|
self.ip = goto_addr;
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -389,3 +395,4 @@ pub const OP_LIST_GET: u16 = 42;
|
||||||
pub const OP_CALL_BUILTIN: u16 = 43;
|
pub const OP_CALL_BUILTIN: u16 = 43;
|
||||||
pub const OP_IF: u16 = 44;
|
pub const OP_IF: u16 = 44;
|
||||||
pub const OP_IF_ELSE: u16 = 45;
|
pub const OP_IF_ELSE: u16 = 45;
|
||||||
|
pub const OP_GOTO_IF: u16 = 46;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue