Begin fixing comiler to work with new instructions
This commit is contained in:
parent
a94cc11069
commit
d83a470638
@ -1,131 +1,38 @@
|
|||||||
use std::{
|
use std::fs::read_to_string;
|
||||||
fmt::{self, Display, Formatter},
|
use std::io::Write;
|
||||||
fs::{File, OpenOptions},
|
|
||||||
io::{stdin, stdout, Read, Write},
|
|
||||||
time::Instant,
|
|
||||||
};
|
|
||||||
|
|
||||||
use clap::{Args, Parser, Subcommand, ValueEnum};
|
use clap::Parser;
|
||||||
use colored::Colorize;
|
use colored::Colorize;
|
||||||
use dust_lang::{compile, display_token_list, format, lex, vm::run_chunk};
|
use dust_lang::{compile, run};
|
||||||
use env_logger::Target;
|
|
||||||
use log::{Level, LevelFilter};
|
use log::{Level, LevelFilter};
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[command(version, author, about)]
|
|
||||||
struct Cli {
|
struct Cli {
|
||||||
#[command(flatten)]
|
/// Source code sent via command line
|
||||||
input: Input,
|
|
||||||
|
|
||||||
#[command(flatten)]
|
|
||||||
output: Output,
|
|
||||||
|
|
||||||
/// Log level: INFO, DEBUG, TRACE, WARN or ERROR. This overrides the DUST_LOG environment variable.
|
|
||||||
#[arg(short, long, value_enum, value_name = "LEVEL", group = "log")]
|
|
||||||
log_level: Option<LevelFilter>,
|
|
||||||
|
|
||||||
/// Path to a file for log output
|
|
||||||
#[arg(long, group = "log")]
|
|
||||||
log_file: Option<String>,
|
|
||||||
|
|
||||||
/// Which CLI feature to use, defaults to "run"
|
|
||||||
#[command(subcommand)]
|
|
||||||
command: Option<Command>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Args, Clone)]
|
|
||||||
#[group(required = true, multiple = true)]
|
|
||||||
struct Input {
|
|
||||||
/// Path to a Dust source file. If this and `input` are not provided, input will be read from
|
|
||||||
/// stdin.
|
|
||||||
source_file: Option<String>,
|
|
||||||
|
|
||||||
/// Dust source code to compile. If this and `source_file` are not provided, input will be read
|
|
||||||
/// from stdin.
|
|
||||||
#[arg(short, long, conflicts_with = "source_file")]
|
|
||||||
input: Option<Vec<u8>>,
|
|
||||||
|
|
||||||
/// The format of the input
|
|
||||||
#[arg(short, long, value_enum, default_value = "source")]
|
|
||||||
read_format: IoFormat,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Args, Clone)]
|
|
||||||
#[group(required = false, multiple = false)]
|
|
||||||
struct Output {
|
|
||||||
/// Path to a file for output. If not provided, output will be written to stdout.
|
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
output_file: Option<String>,
|
command: Option<String>,
|
||||||
|
|
||||||
/// The format of the output
|
/// Whether to output the disassembled chunk
|
||||||
#[arg(short, long, value_enum, default_value = "postcard")]
|
#[arg(short, long)]
|
||||||
write_format: IoFormat,
|
parse: bool,
|
||||||
|
|
||||||
|
/// Whether to style the disassembled chunk
|
||||||
|
#[arg(long)]
|
||||||
|
style_disassembly: Option<bool>,
|
||||||
|
|
||||||
|
/// Log level
|
||||||
|
#[arg(short, long)]
|
||||||
|
log: Option<LevelFilter>,
|
||||||
|
|
||||||
|
/// Path to a source code file
|
||||||
|
path: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Subcommand, Clone, Copy)]
|
fn main() {
|
||||||
enum Command {
|
let args = Cli::parse();
|
||||||
/// Compile Dust to an intermediate format
|
|
||||||
Compile,
|
|
||||||
/// Compile and display the disassembled chunk
|
|
||||||
Disassemble {
|
|
||||||
/// Whether to style the output
|
|
||||||
#[arg(short, long, default_value = "true")]
|
|
||||||
style: bool,
|
|
||||||
},
|
|
||||||
/// Format and display the source code
|
|
||||||
Format {
|
|
||||||
/// Whether to color the output
|
|
||||||
#[arg(short, long, default_value = "true")]
|
|
||||||
color: bool,
|
|
||||||
|
|
||||||
/// Number of spaces per indent level
|
|
||||||
#[arg(short, long, default_value = "4")]
|
|
||||||
indent: usize,
|
|
||||||
|
|
||||||
/// Whether to include line numbers in the output
|
|
||||||
#[arg(short, long, default_value = "true")]
|
|
||||||
line_numbers: bool,
|
|
||||||
},
|
|
||||||
/// Compile and run the Dust code
|
|
||||||
Run,
|
|
||||||
/// Lex and display the token list
|
|
||||||
Tokenize {
|
|
||||||
/// Whether to style the output
|
|
||||||
#[arg(short, long, default_value = "true")]
|
|
||||||
style: bool,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(ValueEnum, Clone, Copy)]
|
|
||||||
enum IoFormat {
|
|
||||||
Json,
|
|
||||||
Postcard,
|
|
||||||
Source,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for IoFormat {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
match self {
|
|
||||||
IoFormat::Json => write!(f, "json"),
|
|
||||||
IoFormat::Postcard => write!(f, "postcard"),
|
|
||||||
IoFormat::Source => write!(f, "source"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() -> Result<(), String> {
|
|
||||||
let Cli {
|
|
||||||
input,
|
|
||||||
output,
|
|
||||||
log_level,
|
|
||||||
log_file,
|
|
||||||
command,
|
|
||||||
} = Cli::parse();
|
|
||||||
let command = command.unwrap_or(Command::Run);
|
|
||||||
let start = Instant::now();
|
|
||||||
let mut logger = env_logger::builder();
|
let mut logger = env_logger::builder();
|
||||||
|
|
||||||
logger.format(move |buf, record| {
|
logger.format(|buf, record| {
|
||||||
let level_display = match record.level() {
|
let level_display = match record.level() {
|
||||||
Level::Info => "INFO".bold().white(),
|
Level::Info => "INFO".bold().white(),
|
||||||
Level::Debug => "DEBUG".bold().blue(),
|
Level::Debug => "DEBUG".bold().blue(),
|
||||||
@ -138,197 +45,53 @@ fn main() -> Result<(), String> {
|
|||||||
.map(|path| path.split("::").last().unwrap_or(path))
|
.map(|path| path.split("::").last().unwrap_or(path))
|
||||||
.unwrap_or("unknown")
|
.unwrap_or("unknown")
|
||||||
.dimmed();
|
.dimmed();
|
||||||
let elapsed = start.elapsed().as_secs_f32();
|
let display = format!("{level_display:5} {module:^6} {args}", args = record.args());
|
||||||
let elapsed_display = format!("T+{elapsed:0.09}").dimmed();
|
|
||||||
let display = format!(
|
|
||||||
"[{elapsed_display}] {level_display:5} {module:^6} {args}",
|
|
||||||
args = record.args()
|
|
||||||
);
|
|
||||||
|
|
||||||
writeln!(buf, "{display}")
|
writeln!(buf, "{display}")
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some(path) = log_file {
|
if let Some(level) = args.log {
|
||||||
let log_file = OpenOptions::new()
|
|
||||||
.create(true)
|
|
||||||
.write(true)
|
|
||||||
.truncate(true)
|
|
||||||
.open(path)
|
|
||||||
.expect("Failed to open log file");
|
|
||||||
|
|
||||||
logger.target(Target::Pipe(Box::new(log_file)));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(level) = log_level {
|
|
||||||
logger.filter_level(level).init();
|
logger.filter_level(level).init();
|
||||||
} else {
|
} else {
|
||||||
logger.parse_env("DUST_LOG").init();
|
logger.parse_env("DUST_LOG").init();
|
||||||
}
|
}
|
||||||
|
|
||||||
let input_bytes = if let Some(source_input) = input.input {
|
let source = if let Some(path) = &args.path {
|
||||||
source_input
|
&read_to_string(path).expect("Failed to read file")
|
||||||
} else if let Some(path) = &input.source_file {
|
} else if let Some(command) = &args.command {
|
||||||
let mut source_file = File::open(path).expect("Failed to open source file");
|
command
|
||||||
let file_length = source_file
|
|
||||||
.metadata()
|
|
||||||
.expect("Failed to read file metadata")
|
|
||||||
.len();
|
|
||||||
let mut buffer = Vec::with_capacity(file_length as usize);
|
|
||||||
|
|
||||||
source_file
|
|
||||||
.read_to_end(&mut buffer)
|
|
||||||
.expect("Failed to read source file");
|
|
||||||
|
|
||||||
buffer
|
|
||||||
} else {
|
} else {
|
||||||
let mut buffer = Vec::new();
|
eprintln!("No input provided");
|
||||||
|
|
||||||
stdin()
|
return;
|
||||||
.read_to_end(&mut buffer)
|
|
||||||
.expect("Failed to read from stdin");
|
|
||||||
|
|
||||||
buffer
|
|
||||||
};
|
};
|
||||||
|
|
||||||
match command {
|
if args.parse {
|
||||||
Command::Format {
|
let style = args.style_disassembly.unwrap_or(true);
|
||||||
color,
|
|
||||||
indent,
|
|
||||||
line_numbers,
|
|
||||||
} => {
|
|
||||||
let source = String::from_utf8(input_bytes).expect("Failed to parse input as UTF-8");
|
|
||||||
let formatted = match format(&source, color, indent, line_numbers) {
|
|
||||||
Ok(formatted) => formatted,
|
|
||||||
Err(dust_error) => {
|
|
||||||
let report = dust_error.report();
|
|
||||||
|
|
||||||
return Err(report);
|
log::info!("Parsing source");
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(path) = output.output_file {
|
match compile(source) {
|
||||||
let mut file = OpenOptions::new()
|
Ok(chunk) => {
|
||||||
.create(true)
|
let disassembly = chunk.disassembler().style(style).disassemble();
|
||||||
.write(true)
|
|
||||||
.truncate(true)
|
|
||||||
.open(path)
|
|
||||||
.expect("Failed to open output file");
|
|
||||||
|
|
||||||
file.write_all(formatted.as_bytes())
|
println!("{}", disassembly);
|
||||||
.expect("Failed to write to output file");
|
|
||||||
} else {
|
|
||||||
println!("{}", formatted);
|
|
||||||
}
|
}
|
||||||
|
Err(error) => {
|
||||||
return Ok(());
|
eprintln!("{}", error.report());
|
||||||
}
|
|
||||||
Command::Tokenize { style } => {
|
|
||||||
let source = String::from_utf8(input_bytes).expect("Failed to parse input as UTF-8");
|
|
||||||
let tokens = match lex(&source) {
|
|
||||||
Ok(tokens) => tokens,
|
|
||||||
Err(dust_error) => {
|
|
||||||
let report = dust_error.report();
|
|
||||||
|
|
||||||
return Err(report);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(path) = output.output_file {
|
|
||||||
let mut file = OpenOptions::new()
|
|
||||||
.create(true)
|
|
||||||
.write(true)
|
|
||||||
.truncate(true)
|
|
||||||
.open(path)
|
|
||||||
.expect("Failed to open output file");
|
|
||||||
|
|
||||||
display_token_list(&tokens, style, &mut file)
|
|
||||||
} else {
|
|
||||||
display_token_list(&tokens, style, &mut stdout())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(());
|
|
||||||
}
|
}
|
||||||
_ => {}
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let chunk = match input.read_format {
|
match run(source) {
|
||||||
IoFormat::Source => {
|
Ok(Some(value)) => println!("{}", value),
|
||||||
let source = String::from_utf8(input_bytes).expect("Failed to parse input as UTF-8");
|
Ok(None) => {}
|
||||||
|
Err(error) => {
|
||||||
match compile(&source) {
|
eprintln!("{}", error.report());
|
||||||
Ok(chunk) => chunk,
|
|
||||||
Err(dust_error) => {
|
|
||||||
let report = dust_error.report();
|
|
||||||
|
|
||||||
return Err(report);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
IoFormat::Json => {
|
|
||||||
serde_json::from_slice(&input_bytes).expect("Failed to deserialize chunk from JSON")
|
|
||||||
}
|
|
||||||
IoFormat::Postcard => {
|
|
||||||
postcard::from_bytes(&input_bytes).expect("Failed to deserialize chunk from Postcard")
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
match command {
|
|
||||||
Command::Run => match run_chunk(&chunk) {
|
|
||||||
Ok(Some(value)) => {
|
|
||||||
println!("{}", value);
|
|
||||||
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
Ok(None) => return Ok(()),
|
|
||||||
Err(dust_error) => {
|
|
||||||
let report = dust_error.report();
|
|
||||||
|
|
||||||
return Err(report);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Command::Disassemble { style } => {
|
|
||||||
let disassembly = chunk.disassembler().style(style).disassemble();
|
|
||||||
|
|
||||||
println!("{disassembly}");
|
|
||||||
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let output_bytes = match output.write_format {
|
|
||||||
IoFormat::Source => {
|
|
||||||
return Err("Invalid options, cannot compile chunk as source code.".to_string())
|
|
||||||
}
|
|
||||||
IoFormat::Json => serde_json::to_vec(&chunk).expect("Failed to serialize chunk as JSON"),
|
|
||||||
IoFormat::Postcard => {
|
|
||||||
let length = postcard::experimental::serialized_size(&chunk)
|
|
||||||
.expect("Failed to calculate Postcard size");
|
|
||||||
let mut buffer = vec![0_u8; length as usize];
|
|
||||||
|
|
||||||
postcard::to_slice(&chunk, &mut buffer).expect("Failed to serialize chunk as Postcard");
|
|
||||||
|
|
||||||
buffer
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(path) = output.output_file {
|
|
||||||
let mut file = OpenOptions::new()
|
|
||||||
.create(true)
|
|
||||||
.write(true)
|
|
||||||
.truncate(true)
|
|
||||||
.open(path)
|
|
||||||
.expect("Failed to open output file");
|
|
||||||
|
|
||||||
file.write_all(&output_bytes)
|
|
||||||
.expect("Failed to write to output file");
|
|
||||||
} else {
|
|
||||||
stdout()
|
|
||||||
.write_all(&output_bytes)
|
|
||||||
.expect("Failed to write to stdout");
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -19,7 +19,7 @@ pub struct Chunk {
|
|||||||
name: Option<String>,
|
name: Option<String>,
|
||||||
r#type: FunctionType,
|
r#type: FunctionType,
|
||||||
|
|
||||||
instructions: Vec<(Instruction, Span)>,
|
instructions: Vec<(Instruction, Type, Span)>,
|
||||||
constants: Vec<ConcreteValue>,
|
constants: Vec<ConcreteValue>,
|
||||||
locals: Vec<Local>,
|
locals: Vec<Local>,
|
||||||
}
|
}
|
||||||
@ -42,7 +42,7 @@ impl Chunk {
|
|||||||
pub fn with_data(
|
pub fn with_data(
|
||||||
name: Option<String>,
|
name: Option<String>,
|
||||||
r#type: FunctionType,
|
r#type: FunctionType,
|
||||||
instructions: Vec<(Instruction, Span)>,
|
instructions: Vec<(Instruction, Type, Span)>,
|
||||||
constants: Vec<ConcreteValue>,
|
constants: Vec<ConcreteValue>,
|
||||||
locals: Vec<Local>,
|
locals: Vec<Local>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
@ -111,15 +111,15 @@ impl Chunk {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn instructions(&self) -> &Vec<(Instruction, Span)> {
|
pub fn instructions(&self) -> &Vec<(Instruction, Type, Span)> {
|
||||||
&self.instructions
|
&self.instructions
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn instructions_mut(&mut self) -> &mut Vec<(Instruction, Span)> {
|
pub fn instructions_mut(&mut self) -> &mut Vec<(Instruction, Type, Span)> {
|
||||||
&mut self.instructions
|
&mut self.instructions
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_instruction(&self, index: usize) -> Result<&(Instruction, Span), ChunkError> {
|
pub fn get_instruction(&self, index: usize) -> Result<&(Instruction, Type, Span), ChunkError> {
|
||||||
self.instructions
|
self.instructions
|
||||||
.get(index)
|
.get(index)
|
||||||
.ok_or(ChunkError::InstructionIndexOutOfBounds { index })
|
.ok_or(ChunkError::InstructionIndexOutOfBounds { index })
|
||||||
|
@ -120,7 +120,7 @@ impl<'src> Compiler<'src> {
|
|||||||
self.chunk
|
self.chunk
|
||||||
.instructions()
|
.instructions()
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|(instruction, _)| {
|
.filter_map(|(instruction, _, _)| {
|
||||||
if instruction.yields_value() {
|
if instruction.yields_value() {
|
||||||
Some(instruction.a() + 1)
|
Some(instruction.a() + 1)
|
||||||
} else {
|
} else {
|
||||||
@ -230,7 +230,7 @@ impl<'src> Compiler<'src> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pop_last_instruction(&mut self) -> Result<(Instruction, Span), CompileError> {
|
fn pop_last_instruction(&mut self) -> Result<(Instruction, Type, Span), CompileError> {
|
||||||
self.chunk
|
self.chunk
|
||||||
.instructions_mut()
|
.instructions_mut()
|
||||||
.pop()
|
.pop()
|
||||||
@ -248,7 +248,7 @@ impl<'src> Compiler<'src> {
|
|||||||
.instructions()
|
.instructions()
|
||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
.map(|(instruction, _)| instruction.operation()),
|
.map(|(instruction, _, _)| instruction.operation()),
|
||||||
) {
|
) {
|
||||||
*nth = operation;
|
*nth = operation;
|
||||||
}
|
}
|
||||||
@ -267,7 +267,7 @@ impl<'src> Compiler<'src> {
|
|||||||
.rev()
|
.rev()
|
||||||
.skip(minimum)
|
.skip(minimum)
|
||||||
.take(maximum)
|
.take(maximum)
|
||||||
.find_map(|(instruction, _)| {
|
.find_map(|(instruction, _, _)| {
|
||||||
if let Operation::LoadBoolean | Operation::LoadConstant = instruction.operation() {
|
if let Operation::LoadBoolean | Operation::LoadConstant = instruction.operation() {
|
||||||
Some(instruction)
|
Some(instruction)
|
||||||
} else {
|
} else {
|
||||||
@ -277,107 +277,133 @@ impl<'src> Compiler<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_instruction_type(&self, instruction: &Instruction) -> Result<Type, CompileError> {
|
pub fn get_instruction_type(&self, instruction: &Instruction) -> Result<Type, CompileError> {
|
||||||
use Operation::*;
|
|
||||||
|
|
||||||
match instruction.operation() {
|
match instruction.operation() {
|
||||||
Add | Divide | Modulo | Multiply | Subtract => {
|
Operation::LoadBoolean => Ok(Type::Boolean),
|
||||||
if instruction.b_is_constant() {
|
Operation::LoadConstant => {
|
||||||
self.chunk
|
let LoadConstant { constant_index, .. } = LoadConstant::from(instruction);
|
||||||
.get_constant_type(instruction.b())
|
|
||||||
.map_err(|error| CompileError::Chunk {
|
|
||||||
error,
|
|
||||||
position: self.current_position,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
self.get_register_type(instruction.b())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LoadBoolean | Not => Ok(Type::Boolean),
|
|
||||||
Negate => {
|
|
||||||
if instruction.b_is_constant() {
|
|
||||||
self.chunk
|
|
||||||
.get_constant_type(instruction.b())
|
|
||||||
.map_err(|error| CompileError::Chunk {
|
|
||||||
error,
|
|
||||||
position: self.current_position,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
self.get_register_type(instruction.b())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LoadConstant => self
|
|
||||||
.chunk
|
|
||||||
.get_constant_type(instruction.b())
|
|
||||||
.map_err(|error| CompileError::Chunk {
|
|
||||||
error,
|
|
||||||
position: self.current_position,
|
|
||||||
}),
|
|
||||||
LoadList => self.get_register_type(instruction.a()),
|
|
||||||
LoadSelf => Ok(Type::SelfChunk),
|
|
||||||
GetLocal => self
|
|
||||||
.chunk
|
|
||||||
.get_local_type(instruction.b())
|
|
||||||
.cloned()
|
|
||||||
.map_err(|error| CompileError::Chunk {
|
|
||||||
error,
|
|
||||||
position: self.current_position,
|
|
||||||
}),
|
|
||||||
Call => {
|
|
||||||
let function_register = instruction.b();
|
|
||||||
let function_type = self.get_register_type(function_register)?;
|
|
||||||
|
|
||||||
match function_type {
|
self.chunk
|
||||||
Type::Function(FunctionType { return_type, .. }) => Ok(*return_type),
|
.get_constant(constant_index)
|
||||||
Type::SelfChunk => {
|
.map(|value| value.r#type())
|
||||||
let return_type = self
|
.map_err(|error| CompileError::Chunk {
|
||||||
.return_type
|
error,
|
||||||
.as_ref()
|
position: self.current_position,
|
||||||
.unwrap_or_else(|| &self.chunk.r#type().return_type);
|
})
|
||||||
|
}
|
||||||
|
Operation::LoadList => {
|
||||||
|
let LoadList { start_register, .. } = LoadList::from(instruction);
|
||||||
|
let first_instruction = self
|
||||||
|
.chunk
|
||||||
|
.get_instruction(start_register as usize)
|
||||||
|
.map_err(|error| CompileError::Chunk {
|
||||||
|
error,
|
||||||
|
position: self.current_position,
|
||||||
|
})?
|
||||||
|
.0; // TODO: Handle the case that the first instruction is Close
|
||||||
|
let item_type = self.get_instruction_type(&first_instruction)?;
|
||||||
|
|
||||||
Ok(return_type.clone())
|
Ok(Type::List(Box::new(item_type)))
|
||||||
}
|
}
|
||||||
_ => Err(CompileError::ExpectedFunctionType {
|
Operation::LoadSelf => Ok(Type::SelfChunk),
|
||||||
|
Operation::GetLocal => {
|
||||||
|
let GetLocal { local_index, .. } = GetLocal::from(instruction);
|
||||||
|
|
||||||
|
self.get_local(local_index)
|
||||||
|
.map(|local| local.r#type.clone())
|
||||||
|
}
|
||||||
|
Operation::Add => {
|
||||||
|
let Add { left, .. } = Add::from(instruction);
|
||||||
|
|
||||||
|
self.get_argument_type(&left)
|
||||||
|
}
|
||||||
|
Operation::Subtract => {
|
||||||
|
let Subtract { left, .. } = Subtract::from(instruction);
|
||||||
|
|
||||||
|
self.get_argument_type(&left)
|
||||||
|
}
|
||||||
|
Operation::Multiply => {
|
||||||
|
let Multiply { left, .. } = Multiply::from(instruction);
|
||||||
|
|
||||||
|
self.get_argument_type(&left)
|
||||||
|
}
|
||||||
|
Operation::Divide => {
|
||||||
|
let Divide { left, .. } = Divide::from(instruction);
|
||||||
|
|
||||||
|
self.get_argument_type(&left)
|
||||||
|
}
|
||||||
|
Operation::Modulo => {
|
||||||
|
let Modulo { left, .. } = Modulo::from(instruction);
|
||||||
|
|
||||||
|
self.get_argument_type(&left)
|
||||||
|
}
|
||||||
|
Operation::Negate => {
|
||||||
|
let Negate { argument, .. } = Negate::from(instruction);
|
||||||
|
|
||||||
|
self.get_argument_type(&argument)
|
||||||
|
}
|
||||||
|
Operation::Not => {
|
||||||
|
let Not { argument, .. } = Not::from(instruction);
|
||||||
|
|
||||||
|
self.get_argument_type(&argument)
|
||||||
|
}
|
||||||
|
Operation::Call => {
|
||||||
|
let Call { function, .. } = Call::from(instruction);
|
||||||
|
let function_type = self.get_argument_type(&function)?;
|
||||||
|
|
||||||
|
if let Type::Function(FunctionType { return_type, .. }) = function_type {
|
||||||
|
Ok(*return_type)
|
||||||
|
} else {
|
||||||
|
Err(CompileError::ExpectedFunctionType {
|
||||||
found: function_type,
|
found: function_type,
|
||||||
position: self.current_position,
|
position: self.current_position,
|
||||||
}),
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CallNative => {
|
Operation::CallNative => {
|
||||||
let native_function = NativeFunction::from(instruction.b());
|
let CallNative { function, .. } = CallNative::from(instruction);
|
||||||
|
|
||||||
Ok(*native_function.r#type().return_type)
|
Ok(*function.r#type().return_type)
|
||||||
}
|
}
|
||||||
_ => Ok(Type::None),
|
|
||||||
|
Operation::Move
|
||||||
|
| Operation::Close
|
||||||
|
| Operation::DefineLocal
|
||||||
|
| Operation::SetLocal
|
||||||
|
| Operation::Test
|
||||||
|
| Operation::TestSet
|
||||||
|
| Operation::Equal
|
||||||
|
| Operation::Less
|
||||||
|
| Operation::LessEqual
|
||||||
|
| Operation::Jump
|
||||||
|
| Operation::Return => Ok(Type::None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_argument_type(&self, argument: &Argument) -> Result<Type, CompileError> {
|
||||||
|
let r#type = match argument {
|
||||||
|
Argument::Register(register_index) => self.get_register_type(*register_index)?,
|
||||||
|
Argument::Constant(constant_index) => self
|
||||||
|
.chunk
|
||||||
|
.get_constant(*constant_index)
|
||||||
|
.map_err(|error| CompileError::Chunk {
|
||||||
|
error,
|
||||||
|
position: self.current_position,
|
||||||
|
})?
|
||||||
|
.r#type(),
|
||||||
|
Argument::Local(local_index) => self.get_local(*local_index)?.r#type.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(r#type)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_register_type(&self, register_index: u16) -> Result<Type, CompileError> {
|
pub fn get_register_type(&self, register_index: u16) -> Result<Type, CompileError> {
|
||||||
for (index, (instruction, _)) in self.chunk.instructions().iter().enumerate() {
|
for (instruction, r#type, _) in self.chunk.instructions() {
|
||||||
if instruction.a() == register_index {
|
if instruction.a() == register_index {
|
||||||
if let Operation::LoadList = instruction.operation() {
|
if let Operation::LoadList = instruction.operation() {
|
||||||
let mut length = (instruction.c() - instruction.b() + 1) as usize;
|
let LoadList { start_register, .. } = LoadList::from(instruction);
|
||||||
let mut item_type = Type::Any;
|
let item_type = self.get_register_type(start_register)?;
|
||||||
let distance_to_end = self.chunk.len() - index;
|
|
||||||
|
|
||||||
for (instruction, _) in self
|
return Ok(Type::List(Box::new(item_type)));
|
||||||
.chunk
|
|
||||||
.instructions()
|
|
||||||
.iter()
|
|
||||||
.rev()
|
|
||||||
.skip(distance_to_end)
|
|
||||||
.take(length)
|
|
||||||
{
|
|
||||||
if let Operation::Close = instruction.operation() {
|
|
||||||
length -= (instruction.c() - instruction.b()) as usize;
|
|
||||||
} else if let Type::Any = item_type {
|
|
||||||
item_type = self.get_instruction_type(instruction)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(Type::List {
|
|
||||||
item_type: Box::new(item_type),
|
|
||||||
length,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Operation::LoadSelf = instruction.operation() {
|
if let Operation::LoadSelf = instruction.operation() {
|
||||||
@ -385,7 +411,7 @@ impl<'src> Compiler<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if instruction.yields_value() {
|
if instruction.yields_value() {
|
||||||
return self.get_instruction_type(instruction);
|
return Ok(r#type.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -425,8 +451,13 @@ impl<'src> Compiler<'src> {
|
|||||||
instruction.operation().to_string().bold(),
|
instruction.operation().to_string().bold(),
|
||||||
position.to_string()
|
position.to_string()
|
||||||
);
|
);
|
||||||
|
let r#type = self
|
||||||
|
.get_instruction_type(&instruction)
|
||||||
|
.unwrap_or(Type::None);
|
||||||
|
|
||||||
self.chunk.instructions_mut().push((instruction, position));
|
self.chunk
|
||||||
|
.instructions_mut()
|
||||||
|
.push((instruction, r#type, position));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn emit_constant(
|
fn emit_constant(
|
||||||
@ -587,9 +618,7 @@ impl<'src> Compiler<'src> {
|
|||||||
|
|
||||||
self.emit_constant(value, position)?;
|
self.emit_constant(value, position)?;
|
||||||
|
|
||||||
self.previous_expression_type = Type::String {
|
self.previous_expression_type = Type::String;
|
||||||
length: Some(text.len()),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
@ -616,7 +645,7 @@ impl<'src> Compiler<'src> {
|
|||||||
self.advance()?;
|
self.advance()?;
|
||||||
self.parse_expression()?;
|
self.parse_expression()?;
|
||||||
|
|
||||||
let (previous_instruction, previous_position) = self.pop_last_instruction()?;
|
let (previous_instruction, _, previous_position) = self.pop_last_instruction()?;
|
||||||
let argument = if let Some(argument) = previous_instruction.destination_as_argument() {
|
let argument = if let Some(argument) = previous_instruction.destination_as_argument() {
|
||||||
argument
|
argument
|
||||||
} else {
|
} else {
|
||||||
@ -654,20 +683,41 @@ impl<'src> Compiler<'src> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_binary_argument(
|
||||||
|
&mut self,
|
||||||
|
instruction: &Instruction,
|
||||||
|
) -> Result<(Argument, bool), CompileError> {
|
||||||
|
let argument = instruction.destination_as_argument().ok_or_else(|| {
|
||||||
|
CompileError::ExpectedExpression {
|
||||||
|
found: self.previous_token.to_owned(),
|
||||||
|
position: self.previous_position,
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
let push_back = matches!(
|
||||||
|
instruction.operation(),
|
||||||
|
Operation::Add
|
||||||
|
| Operation::Subtract
|
||||||
|
| Operation::Multiply
|
||||||
|
| Operation::Divide
|
||||||
|
| Operation::Modulo
|
||||||
|
| Operation::Equal
|
||||||
|
| Operation::Less
|
||||||
|
| Operation::LessEqual
|
||||||
|
| Operation::Test
|
||||||
|
| Operation::TestSet
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok((argument, push_back))
|
||||||
|
}
|
||||||
fn parse_math_binary(&mut self) -> Result<(), CompileError> {
|
fn parse_math_binary(&mut self) -> Result<(), CompileError> {
|
||||||
let (left_instruction, left_position) =
|
let (left_instruction, left_type, left_position) =
|
||||||
self.chunk.instructions_mut().pop().ok_or_else(|| {
|
self.chunk.instructions_mut().pop().ok_or_else(|| {
|
||||||
CompileError::ExpectedExpression {
|
CompileError::ExpectedExpression {
|
||||||
found: self.previous_token.to_owned(),
|
found: self.previous_token.to_owned(),
|
||||||
position: self.previous_position,
|
position: self.previous_position,
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
let left = left_instruction.destination_as_argument().ok_or_else(|| {
|
let (left, push_back_left) = self.handle_binary_argument(&left_instruction)?;
|
||||||
CompileError::ExpectedExpression {
|
|
||||||
found: self.previous_token.to_owned(),
|
|
||||||
position: left_position,
|
|
||||||
}
|
|
||||||
})?;
|
|
||||||
let left_is_mutable_local = if let Argument::Local(local_index) = left {
|
let left_is_mutable_local = if let Argument::Local(local_index) = left {
|
||||||
self.get_local(local_index)?.is_mutable
|
self.get_local(local_index)?.is_mutable
|
||||||
} else {
|
} else {
|
||||||
@ -695,70 +745,24 @@ impl<'src> Compiler<'src> {
|
|||||||
self.advance()?;
|
self.advance()?;
|
||||||
self.parse_sub_expression(&rule.precedence)?;
|
self.parse_sub_expression(&rule.precedence)?;
|
||||||
|
|
||||||
let (right_instruction, right_position) = self.pop_last_instruction()?;
|
let (right_instruction, right_type, right_position) = self.pop_last_instruction()?;
|
||||||
let right = right_instruction.destination_as_argument().ok_or_else(|| {
|
let (right, push_back_right) = self.handle_binary_argument(&right_instruction)?;
|
||||||
CompileError::ExpectedExpression {
|
|
||||||
found: self.previous_token.to_owned(),
|
|
||||||
position: right_position,
|
|
||||||
}
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let destination = if is_assignment {
|
let destination = if is_assignment {
|
||||||
left.index()
|
left.index()
|
||||||
} else {
|
} else {
|
||||||
self.next_register()
|
self.next_register()
|
||||||
};
|
};
|
||||||
let instruction = match operator {
|
let instruction = match operator {
|
||||||
Token::Plus => Instruction::from(Add {
|
Token::Plus => Instruction::add(destination, left, right),
|
||||||
destination,
|
Token::PlusEqual => Instruction::add(destination, left, right),
|
||||||
left,
|
Token::Minus => Instruction::subtract(destination, left, right),
|
||||||
right,
|
Token::MinusEqual => Instruction::subtract(destination, left, right),
|
||||||
}),
|
Token::Star => Instruction::multiply(destination, left, right),
|
||||||
Token::PlusEqual => Instruction::from(Add {
|
Token::StarEqual => Instruction::multiply(destination, left, right),
|
||||||
destination,
|
Token::Slash => Instruction::divide(destination, left, right),
|
||||||
left,
|
Token::SlashEqual => Instruction::divide(destination, left, right),
|
||||||
right,
|
Token::Percent => Instruction::modulo(destination, left, right),
|
||||||
}),
|
Token::PercentEqual => Instruction::modulo(destination, left, right),
|
||||||
Token::Minus => Instruction::from(Subtract {
|
|
||||||
destination,
|
|
||||||
left,
|
|
||||||
right,
|
|
||||||
}),
|
|
||||||
Token::MinusEqual => Instruction::from(Subtract {
|
|
||||||
destination,
|
|
||||||
left,
|
|
||||||
right,
|
|
||||||
}),
|
|
||||||
Token::Star => Instruction::from(Multiply {
|
|
||||||
destination,
|
|
||||||
left,
|
|
||||||
right,
|
|
||||||
}),
|
|
||||||
Token::StarEqual => Instruction::from(Multiply {
|
|
||||||
destination,
|
|
||||||
left,
|
|
||||||
right,
|
|
||||||
}),
|
|
||||||
Token::Slash => Instruction::from(Divide {
|
|
||||||
destination,
|
|
||||||
left,
|
|
||||||
right,
|
|
||||||
}),
|
|
||||||
Token::SlashEqual => Instruction::from(Divide {
|
|
||||||
destination,
|
|
||||||
left,
|
|
||||||
right,
|
|
||||||
}),
|
|
||||||
Token::Percent => Instruction::from(Modulo {
|
|
||||||
destination,
|
|
||||||
left,
|
|
||||||
right,
|
|
||||||
}),
|
|
||||||
Token::PercentEqual => Instruction::from(Modulo {
|
|
||||||
destination,
|
|
||||||
left,
|
|
||||||
right,
|
|
||||||
}),
|
|
||||||
_ => {
|
_ => {
|
||||||
return Err(CompileError::ExpectedTokenMultiple {
|
return Err(CompileError::ExpectedTokenMultiple {
|
||||||
expected: &[
|
expected: &[
|
||||||
@ -779,6 +783,18 @@ impl<'src> Compiler<'src> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if push_back_left {
|
||||||
|
self.chunk
|
||||||
|
.instructions_mut()
|
||||||
|
.push((left_instruction, left_type, left_position));
|
||||||
|
}
|
||||||
|
|
||||||
|
if push_back_right {
|
||||||
|
self.chunk
|
||||||
|
.instructions_mut()
|
||||||
|
.push((right_instruction, right_type, right_position));
|
||||||
|
}
|
||||||
|
|
||||||
self.emit_instruction(instruction, operator_position);
|
self.emit_instruction(instruction, operator_position);
|
||||||
|
|
||||||
if is_assignment {
|
if is_assignment {
|
||||||
@ -799,7 +815,7 @@ impl<'src> Compiler<'src> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let (left_instruction, left_position) =
|
let (left_instruction, left_type, left_position) =
|
||||||
self.chunk.instructions_mut().pop().ok_or_else(|| {
|
self.chunk.instructions_mut().pop().ok_or_else(|| {
|
||||||
CompileError::ExpectedExpression {
|
CompileError::ExpectedExpression {
|
||||||
found: self.previous_token.to_owned(),
|
found: self.previous_token.to_owned(),
|
||||||
@ -819,7 +835,7 @@ impl<'src> Compiler<'src> {
|
|||||||
self.advance()?;
|
self.advance()?;
|
||||||
self.parse_sub_expression(&rule.precedence)?;
|
self.parse_sub_expression(&rule.precedence)?;
|
||||||
|
|
||||||
let (right_instruction, right_position) =
|
let (right_instruction, right_type, right_position) =
|
||||||
self.chunk.instructions_mut().pop().ok_or_else(|| {
|
self.chunk.instructions_mut().pop().ok_or_else(|| {
|
||||||
CompileError::ExpectedExpression {
|
CompileError::ExpectedExpression {
|
||||||
found: self.previous_token.to_owned(),
|
found: self.previous_token.to_owned(),
|
||||||
@ -906,7 +922,7 @@ impl<'src> Compiler<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn parse_logical_binary(&mut self) -> Result<(), CompileError> {
|
fn parse_logical_binary(&mut self) -> Result<(), CompileError> {
|
||||||
let (left_instruction, left_position) = self.pop_last_instruction()?;
|
let (left_instruction, left_type, left_position) = self.pop_last_instruction()?;
|
||||||
|
|
||||||
if !left_instruction.yields_value() {
|
if !left_instruction.yields_value() {
|
||||||
return Err(CompileError::ExpectedExpression {
|
return Err(CompileError::ExpectedExpression {
|
||||||
@ -1049,7 +1065,7 @@ impl<'src> Compiler<'src> {
|
|||||||
Token::Bool => Ok(Type::Boolean),
|
Token::Bool => Ok(Type::Boolean),
|
||||||
Token::FloatKeyword => Ok(Type::Float),
|
Token::FloatKeyword => Ok(Type::Float),
|
||||||
Token::Int => Ok(Type::Integer),
|
Token::Int => Ok(Type::Integer),
|
||||||
Token::Str => Ok(Type::String { length: None }),
|
Token::Str => Ok(Type::String),
|
||||||
_ => Err(CompileError::ExpectedTokenMultiple {
|
_ => Err(CompileError::ExpectedTokenMultiple {
|
||||||
expected: &[
|
expected: &[
|
||||||
TokenKind::Bool,
|
TokenKind::Bool,
|
||||||
@ -1126,10 +1142,7 @@ impl<'src> Compiler<'src> {
|
|||||||
|
|
||||||
self.emit_instruction(load_list, Span(start, end));
|
self.emit_instruction(load_list, Span(start, end));
|
||||||
|
|
||||||
self.previous_expression_type = Type::List {
|
self.previous_expression_type = Type::List(Box::new(item_type));
|
||||||
item_type: Box::new(item_type),
|
|
||||||
length: (destination - start_register) as usize,
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -1150,7 +1163,7 @@ impl<'src> Compiler<'src> {
|
|||||||
self.chunk.instructions_mut().pop();
|
self.chunk.instructions_mut().pop();
|
||||||
self.chunk.instructions_mut().pop();
|
self.chunk.instructions_mut().pop();
|
||||||
self.chunk.instructions_mut().pop();
|
self.chunk.instructions_mut().pop();
|
||||||
} else if let Some((instruction, _)) = self.chunk.instructions().last() {
|
} else if let Some((instruction, _, _)) = self.chunk.instructions().last() {
|
||||||
let test_register = instruction.a();
|
let test_register = instruction.a();
|
||||||
let test = Instruction::from(Test {
|
let test = Instruction::from(Test {
|
||||||
argument: Argument::Register(test_register),
|
argument: Argument::Register(test_register),
|
||||||
@ -1226,7 +1239,7 @@ impl<'src> Compiler<'src> {
|
|||||||
|
|
||||||
self.chunk
|
self.chunk
|
||||||
.instructions_mut()
|
.instructions_mut()
|
||||||
.insert(if_block_end, (jump, self.current_position));
|
.insert(if_block_end, (jump, Type::None, self.current_position));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
2.. => {
|
2.. => {
|
||||||
@ -1238,7 +1251,7 @@ impl<'src> Compiler<'src> {
|
|||||||
|
|
||||||
self.chunk
|
self.chunk
|
||||||
.instructions_mut()
|
.instructions_mut()
|
||||||
.insert(if_block_end, (jump, self.current_position));
|
.insert(if_block_end, (jump, Type::None, self.current_position));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1249,7 +1262,7 @@ impl<'src> Compiler<'src> {
|
|||||||
|
|
||||||
self.chunk
|
self.chunk
|
||||||
.instructions_mut()
|
.instructions_mut()
|
||||||
.insert(if_block_start, (jump, if_block_start_position));
|
.insert(if_block_start, (jump, Type::None, if_block_start_position));
|
||||||
|
|
||||||
if self.chunk.len() >= 4 {
|
if self.chunk.len() >= 4 {
|
||||||
let mut optimizer = Optimizer::new(&mut self.chunk);
|
let mut optimizer = Optimizer::new(&mut self.chunk);
|
||||||
@ -1306,7 +1319,7 @@ impl<'src> Compiler<'src> {
|
|||||||
|
|
||||||
self.chunk
|
self.chunk
|
||||||
.instructions_mut()
|
.instructions_mut()
|
||||||
.insert(block_start, (jump, self.current_position));
|
.insert(block_start, (jump, Type::None, self.current_position));
|
||||||
|
|
||||||
let jump_back_distance = block_end - expression_start + 1;
|
let jump_back_distance = block_end - expression_start + 1;
|
||||||
let jump_back = Instruction::from(Jump {
|
let jump_back = Instruction::from(Jump {
|
||||||
@ -1623,7 +1636,7 @@ impl<'src> Compiler<'src> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn parse_call(&mut self) -> Result<(), CompileError> {
|
fn parse_call(&mut self) -> Result<(), CompileError> {
|
||||||
let (last_instruction, _) =
|
let (last_instruction, _, _) =
|
||||||
self.chunk
|
self.chunk
|
||||||
.instructions()
|
.instructions()
|
||||||
.last()
|
.last()
|
||||||
|
@ -50,8 +50,8 @@ use crate::{value::ConcreteValue, Chunk, Local};
|
|||||||
const INSTRUCTION_HEADER: [&str; 4] = [
|
const INSTRUCTION_HEADER: [&str; 4] = [
|
||||||
"Instructions",
|
"Instructions",
|
||||||
"------------",
|
"------------",
|
||||||
" i POSITION OPERATION INFO ",
|
" i POSITION OPERATION TYPE INFO ",
|
||||||
"--- ---------- ------------- ------------------------------------",
|
"--- ---------- ------------- ---------------- ------------------------------------",
|
||||||
];
|
];
|
||||||
|
|
||||||
const CONSTANT_HEADER: [&str; 4] = [
|
const CONSTANT_HEADER: [&str; 4] = [
|
||||||
@ -255,13 +255,14 @@ impl<'a> Disassembler<'a> {
|
|||||||
self.push_header(line);
|
self.push_header(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (index, (instruction, position)) in self.chunk.instructions().iter().enumerate() {
|
for (index, (instruction, r#type, position)) in self.chunk.instructions().iter().enumerate()
|
||||||
|
{
|
||||||
let position = position.to_string();
|
let position = position.to_string();
|
||||||
let operation = instruction.operation().to_string();
|
let operation = instruction.operation().to_string();
|
||||||
let info = instruction.disassembly_info(self.chunk);
|
let info = instruction.disassembly_info(self.chunk);
|
||||||
|
|
||||||
let instruction_display =
|
let instruction_display =
|
||||||
format!("{index:^3} {position:^10} {operation:13} {info:^36}");
|
format!("{index:^3} {position:^10} {operation:13} {type:^16} {info:^36}");
|
||||||
|
|
||||||
self.push_details(&instruction_display);
|
self.push_details(&instruction_display);
|
||||||
}
|
}
|
||||||
|
@ -121,11 +121,191 @@ impl Instruction {
|
|||||||
Instruction(operation as u64)
|
Instruction(operation as u64)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn r#move(from: u16, to: u16) -> Instruction {
|
||||||
|
Instruction::from(Move { from, to })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn close(from: u16, to: u16) -> Instruction {
|
||||||
|
Instruction::from(Close { from, to })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_boolean(destination: u16, value: bool, jump_next: bool) -> Instruction {
|
||||||
|
Instruction::from(LoadBoolean {
|
||||||
|
destination,
|
||||||
|
value,
|
||||||
|
jump_next,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_constant(destination: u16, constant_index: u16, jump_next: bool) -> Instruction {
|
||||||
|
Instruction::from(LoadConstant {
|
||||||
|
destination,
|
||||||
|
constant_index,
|
||||||
|
jump_next,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_list(destination: u16, start_register: u16) -> Instruction {
|
||||||
|
Instruction::from(LoadList {
|
||||||
|
destination,
|
||||||
|
start_register,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_self(destination: u16) -> Instruction {
|
||||||
|
Instruction::from(LoadSelf { destination })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn define_local(register: u16, local_index: u16, is_mutable: bool) -> Instruction {
|
||||||
|
Instruction::from(DefineLocal {
|
||||||
|
local_index,
|
||||||
|
register,
|
||||||
|
is_mutable,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_local(destination: u16, local_index: u16) -> Instruction {
|
||||||
|
Instruction::from(GetLocal {
|
||||||
|
destination,
|
||||||
|
local_index,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_local(register: u16, local_index: u16) -> Instruction {
|
||||||
|
Instruction::from(SetLocal {
|
||||||
|
local_index,
|
||||||
|
register,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add(destination: u16, left: Argument, right: Argument) -> Instruction {
|
||||||
|
Instruction::from(Add {
|
||||||
|
destination,
|
||||||
|
left,
|
||||||
|
right,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn subtract(destination: u16, left: Argument, right: Argument) -> Instruction {
|
||||||
|
Instruction::from(Subtract {
|
||||||
|
destination,
|
||||||
|
left,
|
||||||
|
right,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn multiply(destination: u16, left: Argument, right: Argument) -> Instruction {
|
||||||
|
Instruction::from(Multiply {
|
||||||
|
destination,
|
||||||
|
left,
|
||||||
|
right,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn divide(destination: u16, left: Argument, right: Argument) -> Instruction {
|
||||||
|
Instruction::from(Divide {
|
||||||
|
destination,
|
||||||
|
left,
|
||||||
|
right,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn modulo(destination: u16, left: Argument, right: Argument) -> Instruction {
|
||||||
|
Instruction::from(Modulo {
|
||||||
|
destination,
|
||||||
|
left,
|
||||||
|
right,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn test(argument: Argument, value: bool) -> Instruction {
|
||||||
|
Instruction::from(Test { argument, value })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn test_set(destination: u16, argument: Argument, value: bool) -> Instruction {
|
||||||
|
Instruction::from(TestSet {
|
||||||
|
destination,
|
||||||
|
argument,
|
||||||
|
value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn equal(value: bool, left: Argument, right: Argument) -> Instruction {
|
||||||
|
Instruction::from(Equal { value, left, right })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn less(value: bool, left: Argument, right: Argument) -> Instruction {
|
||||||
|
Instruction::from(Less { value, left, right })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn less_equal(value: bool, left: Argument, right: Argument) -> Instruction {
|
||||||
|
Instruction::from(LessEqual { value, left, right })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn negate(destination: u16, argument: Argument) -> Instruction {
|
||||||
|
Instruction::from(Negate {
|
||||||
|
destination,
|
||||||
|
argument,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn not(destination: u16, argument: Argument) -> Instruction {
|
||||||
|
Instruction::from(Not {
|
||||||
|
destination,
|
||||||
|
argument,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn jump(offset: u16, is_positive: bool) -> Instruction {
|
||||||
|
Instruction::from(Jump {
|
||||||
|
offset,
|
||||||
|
is_positive,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn call(destination: u16, function: Argument, argument_count: u16) -> Instruction {
|
||||||
|
Instruction::from(Call {
|
||||||
|
destination,
|
||||||
|
function,
|
||||||
|
argument_count,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn call_native(
|
||||||
|
destination: u16,
|
||||||
|
function: NativeFunction,
|
||||||
|
argument_count: u16,
|
||||||
|
) -> Instruction {
|
||||||
|
Instruction::from(CallNative {
|
||||||
|
destination,
|
||||||
|
function,
|
||||||
|
argument_count,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn r#return(should_return_value: bool) -> Instruction {
|
||||||
|
Instruction::from(Return {
|
||||||
|
should_return_value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn destination_as_argument(&self) -> Option<Argument> {
|
pub fn destination_as_argument(&self) -> Option<Argument> {
|
||||||
if self.yields_value() {
|
match self.operation() {
|
||||||
Some(Argument::Register(self.a()))
|
Operation::LoadConstant => Some(Argument::Constant(self.b())),
|
||||||
} else {
|
Operation::GetLocal => Some(Argument::Local(self.b())),
|
||||||
None
|
Operation::LoadBoolean
|
||||||
|
| Operation::LoadList
|
||||||
|
| Operation::LoadSelf
|
||||||
|
| Operation::Add
|
||||||
|
| Operation::Subtract
|
||||||
|
| Operation::Multiply
|
||||||
|
| Operation::Divide
|
||||||
|
| Operation::Modulo
|
||||||
|
| Operation::Negate
|
||||||
|
| Operation::Not
|
||||||
|
| Operation::Call
|
||||||
|
| Operation::CallNative => Some(Argument::Register(self.a())),
|
||||||
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,27 +464,25 @@ impl Instruction {
|
|||||||
| Operation::Multiply
|
| Operation::Multiply
|
||||||
| Operation::Divide
|
| Operation::Divide
|
||||||
| Operation::Modulo
|
| Operation::Modulo
|
||||||
| Operation::Equal
|
|
||||||
| Operation::Less
|
|
||||||
| Operation::LessEqual
|
|
||||||
| Operation::Negate
|
| Operation::Negate
|
||||||
| Operation::Not
|
| Operation::Not
|
||||||
| Operation::Call => true,
|
| Operation::Call => true,
|
||||||
|
|
||||||
Operation::CallNative => {
|
Operation::CallNative => {
|
||||||
let function = NativeFunction::from(self.b());
|
let function = NativeFunction::from(self.b());
|
||||||
|
|
||||||
function.returns_value()
|
function.returns_value()
|
||||||
}
|
}
|
||||||
|
|
||||||
Operation::Move
|
Operation::Move
|
||||||
| Operation::Close
|
| Operation::Close
|
||||||
| Operation::DefineLocal
|
| Operation::DefineLocal
|
||||||
| Operation::SetLocal
|
| Operation::SetLocal
|
||||||
|
| Operation::Equal
|
||||||
|
| Operation::Less
|
||||||
|
| Operation::LessEqual
|
||||||
| Operation::Test
|
| Operation::Test
|
||||||
| Operation::TestSet
|
| Operation::TestSet
|
||||||
| Operation::Jump
|
| Operation::Jump
|
||||||
| Operation::Return => true,
|
| Operation::Return => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -313,12 +491,12 @@ impl Instruction {
|
|||||||
Operation::Move => {
|
Operation::Move => {
|
||||||
let Move { from, to } = Move::from(self);
|
let Move { from, to } = Move::from(self);
|
||||||
|
|
||||||
format!("{to} = {from}")
|
format!("R{to} = R{from}")
|
||||||
}
|
}
|
||||||
Operation::Close => {
|
Operation::Close => {
|
||||||
let Close { from, to } = Close::from(self);
|
let Close { from, to } = Close::from(self);
|
||||||
|
|
||||||
format!("{from}..={to}")
|
format!("R{from}..=R{to}")
|
||||||
}
|
}
|
||||||
Operation::LoadBoolean => {
|
Operation::LoadBoolean => {
|
||||||
let LoadBoolean {
|
let LoadBoolean {
|
||||||
@ -336,14 +514,14 @@ impl Instruction {
|
|||||||
Operation::LoadConstant => {
|
Operation::LoadConstant => {
|
||||||
let LoadConstant {
|
let LoadConstant {
|
||||||
destination,
|
destination,
|
||||||
constant_index: constant,
|
constant_index,
|
||||||
jump_next,
|
jump_next,
|
||||||
} = LoadConstant::from(self);
|
} = LoadConstant::from(self);
|
||||||
|
|
||||||
if jump_next {
|
if jump_next {
|
||||||
format!("R{destination} = C{constant} JUMP +1")
|
format!("R{destination} = C{constant_index} JUMP +1")
|
||||||
} else {
|
} else {
|
||||||
format!("R{destination} = C{constant}")
|
format!("R{destination} = C{constant_index}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Operation::LoadList => {
|
Operation::LoadList => {
|
||||||
|
@ -30,7 +30,7 @@ pub use crate::r#type::{EnumType, FunctionType, StructType, Type, TypeConflict};
|
|||||||
pub use crate::scope::Scope;
|
pub use crate::scope::Scope;
|
||||||
pub use crate::token::{display_token_list, Token, TokenKind, TokenOwned};
|
pub use crate::token::{display_token_list, Token, TokenKind, TokenOwned};
|
||||||
pub use crate::value::{AbstractValue, ConcreteValue, RangeValue, Value, ValueError, ValueRef};
|
pub use crate::value::{AbstractValue, ConcreteValue, RangeValue, Value, ValueError, ValueRef};
|
||||||
pub use crate::vm::{run_source, Vm, VmError};
|
pub use crate::vm::{run, Vm, VmError};
|
||||||
|
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
@ -148,7 +148,7 @@ define_native_function! {
|
|||||||
FunctionType {
|
FunctionType {
|
||||||
type_parameters: None,
|
type_parameters: None,
|
||||||
value_parameters: Some(vec![(0, Type::Any)]),
|
value_parameters: Some(vec![(0, Type::Any)]),
|
||||||
return_type: Box::new(Type::String { length: None })
|
return_type: Box::new(Type::String)
|
||||||
},
|
},
|
||||||
logic::to_string
|
logic::to_string
|
||||||
),
|
),
|
||||||
@ -209,7 +209,7 @@ define_native_function! {
|
|||||||
FunctionType {
|
FunctionType {
|
||||||
type_parameters: None,
|
type_parameters: None,
|
||||||
value_parameters: None,
|
value_parameters: None,
|
||||||
return_type: Box::new(Type::String { length: None })
|
return_type: Box::new(Type::String)
|
||||||
},
|
},
|
||||||
logic::read_line
|
logic::read_line
|
||||||
),
|
),
|
||||||
@ -224,7 +224,7 @@ define_native_function! {
|
|||||||
"write",
|
"write",
|
||||||
FunctionType {
|
FunctionType {
|
||||||
type_parameters: None,
|
type_parameters: None,
|
||||||
value_parameters: Some(vec![(0, Type::String { length: None })]),
|
value_parameters: Some(vec![(0, Type::String)]),
|
||||||
return_type: Box::new(Type::None)
|
return_type: Box::new(Type::None)
|
||||||
},
|
},
|
||||||
logic::write
|
logic::write
|
||||||
@ -236,7 +236,7 @@ define_native_function! {
|
|||||||
"write_line",
|
"write_line",
|
||||||
FunctionType {
|
FunctionType {
|
||||||
type_parameters: None,
|
type_parameters: None,
|
||||||
value_parameters: Some(vec![(0, Type::String { length: None })]),
|
value_parameters: Some(vec![(0, Type::String)]),
|
||||||
return_type: Box::new(Type::None)
|
return_type: Box::new(Type::None)
|
||||||
},
|
},
|
||||||
logic::write_line
|
logic::write_line
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//! Tool used by the compiler to optimize a chunk's bytecode.
|
//! Tool used by the compiler to optimize a chunk's bytecode.
|
||||||
|
|
||||||
use crate::{Chunk, Instruction, Operation, Span};
|
use crate::{Chunk, Instruction, Operation, Span, Type};
|
||||||
|
|
||||||
/// An instruction optimizer that mutably borrows instructions from a chunk.
|
/// An instruction optimizer that mutably borrows instructions from a chunk.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -93,7 +93,7 @@ impl<'a> Optimizer<'a> {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn instructions_mut(&mut self) -> &mut Vec<(Instruction, Span)> {
|
fn instructions_mut(&mut self) -> &mut Vec<(Instruction, Type, Span)> {
|
||||||
self.chunk.instructions_mut()
|
self.chunk.instructions_mut()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,7 +109,7 @@ impl<'a> Optimizer<'a> {
|
|||||||
.instructions()
|
.instructions()
|
||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
.map(|(instruction, _)| instruction.operation()),
|
.map(|(instruction, _, _)| instruction.operation()),
|
||||||
) {
|
) {
|
||||||
*nth = operation;
|
*nth = operation;
|
||||||
}
|
}
|
||||||
|
@ -22,14 +22,7 @@ pub enum Type {
|
|||||||
concrete_type: Option<Box<Type>>,
|
concrete_type: Option<Box<Type>>,
|
||||||
},
|
},
|
||||||
Integer,
|
Integer,
|
||||||
List {
|
List(Box<Type>),
|
||||||
item_type: Box<Type>,
|
|
||||||
length: usize,
|
|
||||||
},
|
|
||||||
ListEmpty,
|
|
||||||
ListOf {
|
|
||||||
item_type: Box<Type>,
|
|
||||||
},
|
|
||||||
Map {
|
Map {
|
||||||
pairs: HashMap<u8, Type>,
|
pairs: HashMap<u8, Type>,
|
||||||
},
|
},
|
||||||
@ -38,9 +31,7 @@ pub enum Type {
|
|||||||
r#type: Box<Type>,
|
r#type: Box<Type>,
|
||||||
},
|
},
|
||||||
SelfChunk,
|
SelfChunk,
|
||||||
String {
|
String,
|
||||||
length: Option<usize>,
|
|
||||||
},
|
|
||||||
Struct(StructType),
|
Struct(StructType),
|
||||||
Tuple {
|
Tuple {
|
||||||
fields: Option<Vec<Type>>,
|
fields: Option<Vec<Type>>,
|
||||||
@ -106,23 +97,7 @@ impl Type {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(
|
(Type::List(left_type), Type::List(right_type)) => {
|
||||||
Type::List {
|
|
||||||
item_type: left_type,
|
|
||||||
length: left_length,
|
|
||||||
},
|
|
||||||
Type::List {
|
|
||||||
item_type: right_type,
|
|
||||||
length: right_length,
|
|
||||||
},
|
|
||||||
) => {
|
|
||||||
if left_length != right_length {
|
|
||||||
return Err(TypeConflict {
|
|
||||||
actual: other.clone(),
|
|
||||||
expected: self.clone(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if left_type.check(right_type).is_err() {
|
if left_type.check(right_type).is_err() {
|
||||||
return Err(TypeConflict {
|
return Err(TypeConflict {
|
||||||
actual: other.clone(),
|
actual: other.clone(),
|
||||||
@ -132,51 +107,6 @@ impl Type {
|
|||||||
|
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
(
|
|
||||||
Type::ListOf {
|
|
||||||
item_type: left_type,
|
|
||||||
},
|
|
||||||
Type::ListOf {
|
|
||||||
item_type: right_type,
|
|
||||||
},
|
|
||||||
) => {
|
|
||||||
if left_type.check(right_type).is_err() {
|
|
||||||
return Err(TypeConflict {
|
|
||||||
actual: other.clone(),
|
|
||||||
expected: self.clone(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(
|
|
||||||
Type::List {
|
|
||||||
item_type: list_item_type,
|
|
||||||
..
|
|
||||||
},
|
|
||||||
Type::ListOf {
|
|
||||||
item_type: list_of_item_type,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
| (
|
|
||||||
Type::ListOf {
|
|
||||||
item_type: list_of_item_type,
|
|
||||||
},
|
|
||||||
Type::List {
|
|
||||||
item_type: list_item_type,
|
|
||||||
..
|
|
||||||
},
|
|
||||||
) => {
|
|
||||||
// TODO: This is a hack, remove it.
|
|
||||||
if let Type::Any = **list_of_item_type {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
if list_item_type.check(list_of_item_type).is_err() {
|
|
||||||
return Err(TypeConflict {
|
|
||||||
actual: other.clone(),
|
|
||||||
expected: self.clone(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(
|
(
|
||||||
Type::Function(FunctionType {
|
Type::Function(FunctionType {
|
||||||
type_parameters: left_type_parameters,
|
type_parameters: left_type_parameters,
|
||||||
@ -237,9 +167,7 @@ impl Display for Type {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Type::Integer => write!(f, "int"),
|
Type::Integer => write!(f, "int"),
|
||||||
Type::List { item_type, length } => write!(f, "[{item_type}; {length}]"),
|
Type::List(item_type) => write!(f, "[{item_type}]"),
|
||||||
Type::ListEmpty => write!(f, "[]"),
|
|
||||||
Type::ListOf { item_type } => write!(f, "[{item_type}]"),
|
|
||||||
Type::Map { pairs } => {
|
Type::Map { pairs } => {
|
||||||
write!(f, "map ")?;
|
write!(f, "map ")?;
|
||||||
|
|
||||||
@ -258,7 +186,7 @@ impl Display for Type {
|
|||||||
Type::None => write!(f, "none"),
|
Type::None => write!(f, "none"),
|
||||||
Type::Range { r#type } => write!(f, "{type} range"),
|
Type::Range { r#type } => write!(f, "{type} range"),
|
||||||
Type::SelfChunk => write!(f, "self"),
|
Type::SelfChunk => write!(f, "self"),
|
||||||
Type::String { .. } => write!(f, "str"),
|
Type::String => write!(f, "str"),
|
||||||
Type::Struct(struct_type) => write!(f, "{struct_type}"),
|
Type::Struct(struct_type) => write!(f, "{struct_type}"),
|
||||||
Type::Tuple { fields } => {
|
Type::Tuple { fields } => {
|
||||||
if let Some(fields) = fields {
|
if let Some(fields) = fields {
|
||||||
@ -310,34 +238,10 @@ impl Ord for Type {
|
|||||||
(Type::Generic { .. }, _) => Ordering::Greater,
|
(Type::Generic { .. }, _) => Ordering::Greater,
|
||||||
(Type::Integer, Type::Integer) => Ordering::Equal,
|
(Type::Integer, Type::Integer) => Ordering::Equal,
|
||||||
(Type::Integer, _) => Ordering::Greater,
|
(Type::Integer, _) => Ordering::Greater,
|
||||||
(
|
(Type::List(left_item_type), Type::List(right_item_type)) => {
|
||||||
Type::List {
|
left_item_type.cmp(right_item_type)
|
||||||
item_type: left_item_type,
|
|
||||||
length: left_length,
|
|
||||||
},
|
|
||||||
Type::List {
|
|
||||||
item_type: right_item_type,
|
|
||||||
length: right_length,
|
|
||||||
},
|
|
||||||
) => {
|
|
||||||
if left_length == right_length {
|
|
||||||
left_item_type.cmp(right_item_type)
|
|
||||||
} else {
|
|
||||||
left_length.cmp(right_length)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
(Type::List { .. }, _) => Ordering::Greater,
|
(Type::List { .. }, _) => Ordering::Greater,
|
||||||
(Type::ListEmpty, Type::ListEmpty) => Ordering::Equal,
|
|
||||||
(Type::ListEmpty, _) => Ordering::Greater,
|
|
||||||
(
|
|
||||||
Type::ListOf {
|
|
||||||
item_type: left_item_type,
|
|
||||||
},
|
|
||||||
Type::ListOf {
|
|
||||||
item_type: right_item_type,
|
|
||||||
},
|
|
||||||
) => left_item_type.cmp(right_item_type),
|
|
||||||
(Type::ListOf { .. }, _) => Ordering::Greater,
|
|
||||||
(Type::Map { pairs: left_pairs }, Type::Map { pairs: right_pairs }) => {
|
(Type::Map { pairs: left_pairs }, Type::Map { pairs: right_pairs }) => {
|
||||||
left_pairs.iter().cmp(right_pairs.iter())
|
left_pairs.iter().cmp(right_pairs.iter())
|
||||||
}
|
}
|
||||||
@ -350,7 +254,7 @@ impl Ord for Type {
|
|||||||
(Type::Range { .. }, _) => Ordering::Greater,
|
(Type::Range { .. }, _) => Ordering::Greater,
|
||||||
(Type::SelfChunk, Type::SelfChunk) => Ordering::Equal,
|
(Type::SelfChunk, Type::SelfChunk) => Ordering::Equal,
|
||||||
(Type::SelfChunk, _) => Ordering::Greater,
|
(Type::SelfChunk, _) => Ordering::Greater,
|
||||||
(Type::String { length: left }, Type::String { length: right }) => left.cmp(right),
|
(Type::String, Type::String) => Ordering::Equal,
|
||||||
(Type::String { .. }, _) => Ordering::Greater,
|
(Type::String { .. }, _) => Ordering::Greater,
|
||||||
(Type::Struct(left_struct), Type::Struct(right_struct)) => {
|
(Type::Struct(left_struct), Type::Struct(right_struct)) => {
|
||||||
left_struct.cmp(right_struct)
|
left_struct.cmp(right_struct)
|
||||||
|
@ -47,15 +47,10 @@ impl ConcreteValue {
|
|||||||
ConcreteValue::List(list) => {
|
ConcreteValue::List(list) => {
|
||||||
let item_type = list.first().map_or(Type::Any, |item| item.r#type());
|
let item_type = list.first().map_or(Type::Any, |item| item.r#type());
|
||||||
|
|
||||||
Type::List {
|
Type::List(Box::new(item_type))
|
||||||
item_type: Box::new(item_type),
|
|
||||||
length: list.len(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ConcreteValue::Range(range) => range.r#type(),
|
ConcreteValue::Range(range) => range.r#type(),
|
||||||
ConcreteValue::String(string) => Type::String {
|
ConcreteValue::String(_) => Type::String,
|
||||||
length: Some(string.len()),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ use crate::{
|
|||||||
Instruction, NativeFunction, NativeFunctionError, Operation, Span, Value, ValueError, ValueRef,
|
Instruction, NativeFunction, NativeFunctionError, Operation, Span, Value, ValueError, ValueRef,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn run_source(source: &str) -> Result<Option<ConcreteValue>, DustError> {
|
pub fn run(source: &str) -> Result<Option<ConcreteValue>, DustError> {
|
||||||
let chunk = compile(source)?;
|
let chunk = compile(source)?;
|
||||||
let mut vm = Vm::new(&chunk, None);
|
let mut vm = Vm::new(&chunk, None);
|
||||||
|
|
||||||
@ -18,13 +18,6 @@ pub fn run_source(source: &str) -> Result<Option<ConcreteValue>, DustError> {
|
|||||||
.map_err(|error| DustError::Runtime { error, source })
|
.map_err(|error| DustError::Runtime { error, source })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_chunk(chunk: &Chunk) -> Result<Option<ConcreteValue>, DustError> {
|
|
||||||
let mut vm = Vm::new(chunk, None);
|
|
||||||
|
|
||||||
vm.run()
|
|
||||||
.map_err(|error| DustError::Runtime { error, source: "" })
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Dust virtual machine.
|
/// Dust virtual machine.
|
||||||
///
|
///
|
||||||
/// See the [module-level documentation](index.html) for more information.
|
/// See the [module-level documentation](index.html) for more information.
|
||||||
@ -617,19 +610,18 @@ impl<'a> Vm<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn read(&mut self) -> Result<Instruction, VmError> {
|
fn read(&mut self) -> Result<Instruction, VmError> {
|
||||||
let (instruction, position) =
|
let (instruction, _type, position) =
|
||||||
self.chunk
|
self.chunk
|
||||||
.get_instruction(self.ip)
|
.get_instruction(self.ip)
|
||||||
.copied()
|
|
||||||
.map_err(|error| VmError::Chunk {
|
.map_err(|error| VmError::Chunk {
|
||||||
error,
|
error,
|
||||||
position: self.current_position,
|
position: self.current_position,
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
self.ip += 1;
|
self.ip += 1;
|
||||||
self.current_position = position;
|
self.current_position = *position;
|
||||||
|
|
||||||
Ok(instruction)
|
Ok(*instruction)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ fn constant() {
|
|||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run_source(source), Ok(Some(ConcreteValue::Integer(42))));
|
assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(42))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -43,7 +43,7 @@ fn empty() {
|
|||||||
vec![]
|
vec![]
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
assert_eq!(run_source(source), Ok(None));
|
assert_eq!(run(source), Ok(None));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -61,13 +61,11 @@ fn parentheses_precedence() {
|
|||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
*Instruction::add(0, 0, 1)
|
Instruction::add(0, Argument::Constant(0), Argument::Constant(1)),
|
||||||
.set_b_is_constant()
|
|
||||||
.set_c_is_constant(),
|
|
||||||
Span(3, 4)
|
Span(3, 4)
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
*Instruction::multiply(1, 0, 2).set_c_is_constant(),
|
Instruction::multiply(1, Argument::Register(0), Argument::Constant(2)),
|
||||||
Span(8, 9)
|
Span(8, 9)
|
||||||
),
|
),
|
||||||
(Instruction::r#return(true), Span(11, 11)),
|
(Instruction::r#return(true), Span(11, 11)),
|
||||||
@ -81,5 +79,5 @@ fn parentheses_precedence() {
|
|||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run_source(source), Ok(Some(ConcreteValue::Integer(9))));
|
assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(9))));
|
||||||
}
|
}
|
||||||
|
@ -15,9 +15,7 @@ fn equal() {
|
|||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
*Instruction::equal(true, 0, 1)
|
Instruction::equal(true, Argument::Constant(0), Argument::Constant(1)),
|
||||||
.set_b_is_constant()
|
|
||||||
.set_c_is_constant(),
|
|
||||||
Span(2, 4)
|
Span(2, 4)
|
||||||
),
|
),
|
||||||
(Instruction::jump(1, true), Span(2, 4)),
|
(Instruction::jump(1, true), Span(2, 4)),
|
||||||
@ -30,7 +28,7 @@ fn equal() {
|
|||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run_source(source), Ok(Some(ConcreteValue::Boolean(false))));
|
assert_eq!(run(source), Ok(Some(ConcreteValue::Boolean(false))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -48,9 +46,7 @@ fn greater() {
|
|||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
*Instruction::less_equal(false, 0, 1)
|
Instruction::less_equal(false, Argument::Constant(0), Argument::Constant(1)),
|
||||||
.set_b_is_constant()
|
|
||||||
.set_c_is_constant(),
|
|
||||||
Span(2, 3)
|
Span(2, 3)
|
||||||
),
|
),
|
||||||
(Instruction::jump(1, true), Span(2, 3)),
|
(Instruction::jump(1, true), Span(2, 3)),
|
||||||
@ -63,7 +59,7 @@ fn greater() {
|
|||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run_source(source), Ok(Some(ConcreteValue::Boolean(false))));
|
assert_eq!(run(source), Ok(Some(ConcreteValue::Boolean(false))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -81,9 +77,7 @@ fn greater_than_or_equal() {
|
|||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
*Instruction::less(false, 0, 1)
|
Instruction::less(false, Argument::Constant(0), Argument::Constant(1)),
|
||||||
.set_b_is_constant()
|
|
||||||
.set_c_is_constant(),
|
|
||||||
Span(2, 4)
|
Span(2, 4)
|
||||||
),
|
),
|
||||||
(Instruction::jump(1, true), Span(2, 4)),
|
(Instruction::jump(1, true), Span(2, 4)),
|
||||||
@ -96,7 +90,7 @@ fn greater_than_or_equal() {
|
|||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run_source(source), Ok(Some(ConcreteValue::Boolean(false))));
|
assert_eq!(run(source), Ok(Some(ConcreteValue::Boolean(false))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -114,9 +108,7 @@ fn less_than() {
|
|||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
*Instruction::less(true, 0, 1)
|
Instruction::less(true, Argument::Constant(0), Argument::Constant(1)),
|
||||||
.set_b_is_constant()
|
|
||||||
.set_c_is_constant(),
|
|
||||||
Span(2, 3)
|
Span(2, 3)
|
||||||
),
|
),
|
||||||
(Instruction::jump(1, true), Span(2, 3)),
|
(Instruction::jump(1, true), Span(2, 3)),
|
||||||
@ -129,7 +121,7 @@ fn less_than() {
|
|||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run_source(source), Ok(Some(ConcreteValue::Boolean(true))));
|
assert_eq!(run(source), Ok(Some(ConcreteValue::Boolean(true))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -147,9 +139,7 @@ fn less_than_or_equal() {
|
|||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
*Instruction::less_equal(true, 0, 1)
|
Instruction::less_equal(true, Argument::Constant(0), Argument::Constant(1)),
|
||||||
.set_b_is_constant()
|
|
||||||
.set_c_is_constant(),
|
|
||||||
Span(2, 4)
|
Span(2, 4)
|
||||||
),
|
),
|
||||||
(Instruction::jump(1, true), Span(2, 4)),
|
(Instruction::jump(1, true), Span(2, 4)),
|
||||||
@ -162,7 +152,7 @@ fn less_than_or_equal() {
|
|||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run_source(source), Ok(Some(ConcreteValue::Boolean(true))));
|
assert_eq!(run(source), Ok(Some(ConcreteValue::Boolean(true))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -180,9 +170,7 @@ fn not_equal() {
|
|||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
*Instruction::equal(false, 0, 1)
|
Instruction::equal(false, Argument::Constant(0), Argument::Constant(1)),
|
||||||
.set_b_is_constant()
|
|
||||||
.set_c_is_constant(),
|
|
||||||
Span(2, 4)
|
Span(2, 4)
|
||||||
),
|
),
|
||||||
(Instruction::jump(1, true), Span(2, 4)),
|
(Instruction::jump(1, true), Span(2, 4)),
|
||||||
@ -195,5 +183,5 @@ fn not_equal() {
|
|||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run_source(source), Ok(Some(ConcreteValue::Boolean(true))));
|
assert_eq!(run(source), Ok(Some(ConcreteValue::Boolean(true))));
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ fn function() {
|
|||||||
let source = "fn(a: int, b: int) -> int { a + b }";
|
let source = "fn(a: int, b: int) -> int { a + b }";
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
run_source(source),
|
run(source),
|
||||||
Ok(Some(ConcreteValue::Function(Chunk::with_data(
|
Ok(Some(ConcreteValue::Function(Chunk::with_data(
|
||||||
None,
|
None,
|
||||||
FunctionType {
|
FunctionType {
|
||||||
@ -18,7 +18,10 @@ fn function() {
|
|||||||
}))
|
}))
|
||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(Instruction::add(2, 0, 1), Span(30, 31)),
|
(
|
||||||
|
Instruction::add(2, Argument::Local(0), Argument::Local(1)),
|
||||||
|
Span(30, 31)
|
||||||
|
),
|
||||||
(Instruction::r#return(true), Span(35, 35)),
|
(Instruction::r#return(true), Span(35, 35)),
|
||||||
],
|
],
|
||||||
vec![ConcreteValue::string("a"), ConcreteValue::string("b"),],
|
vec![ConcreteValue::string("a"), ConcreteValue::string("b"),],
|
||||||
@ -47,7 +50,7 @@ fn function_call() {
|
|||||||
(Instruction::load_constant(0, 0, false), Span(0, 36)),
|
(Instruction::load_constant(0, 0, false), Span(0, 36)),
|
||||||
(Instruction::load_constant(1, 1, false), Span(36, 37)),
|
(Instruction::load_constant(1, 1, false), Span(36, 37)),
|
||||||
(Instruction::load_constant(2, 2, false), Span(39, 40)),
|
(Instruction::load_constant(2, 2, false), Span(39, 40)),
|
||||||
(Instruction::call(3, 0, 2), Span(35, 41)),
|
(Instruction::call(3, Argument::Constant(0), 2), Span(35, 41)),
|
||||||
(Instruction::r#return(true), Span(41, 41)),
|
(Instruction::r#return(true), Span(41, 41)),
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
@ -59,7 +62,10 @@ fn function_call() {
|
|||||||
return_type: Box::new(Type::Integer)
|
return_type: Box::new(Type::Integer)
|
||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(Instruction::add(2, 0, 1), Span(30, 31)),
|
(
|
||||||
|
Instruction::add(2, Argument::Local(0), Argument::Local(1)),
|
||||||
|
Span(30, 31)
|
||||||
|
),
|
||||||
(Instruction::r#return(true), Span(35, 36)),
|
(Instruction::r#return(true), Span(35, 36)),
|
||||||
],
|
],
|
||||||
vec![ConcreteValue::string("a"), ConcreteValue::string("b"),],
|
vec![ConcreteValue::string("a"), ConcreteValue::string("b"),],
|
||||||
@ -75,7 +81,7 @@ fn function_call() {
|
|||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run_source(source), Ok(Some(ConcreteValue::Integer(3))));
|
assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(3))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -105,7 +111,10 @@ fn function_declaration() {
|
|||||||
return_type: Box::new(Type::Integer)
|
return_type: Box::new(Type::Integer)
|
||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(Instruction::add(2, 0, 1), Span(35, 36)),
|
(
|
||||||
|
Instruction::add(2, Argument::Local(0), Argument::Local(1)),
|
||||||
|
Span(35, 36)
|
||||||
|
),
|
||||||
(Instruction::r#return(true), Span(40, 40)),
|
(Instruction::r#return(true), Span(40, 40)),
|
||||||
],
|
],
|
||||||
vec![ConcreteValue::string("a"), ConcreteValue::string("b")],
|
vec![ConcreteValue::string("a"), ConcreteValue::string("b")],
|
||||||
@ -129,5 +138,5 @@ fn function_declaration() {
|
|||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run_source(source), Ok(None));
|
assert_eq!(run(source), Ok(None));
|
||||||
}
|
}
|
||||||
|
@ -11,10 +11,7 @@ fn empty_list() {
|
|||||||
FunctionType {
|
FunctionType {
|
||||||
type_parameters: None,
|
type_parameters: None,
|
||||||
value_parameters: None,
|
value_parameters: None,
|
||||||
return_type: Box::new(Type::List {
|
return_type: Box::new(Type::List(Box::new(Type::Any))),
|
||||||
item_type: Box::new(Type::Any),
|
|
||||||
length: 0
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(Instruction::load_list(0, 0), Span(0, 2)),
|
(Instruction::load_list(0, 0), Span(0, 2)),
|
||||||
@ -25,7 +22,7 @@ fn empty_list() {
|
|||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run_source(source), Ok(Some(ConcreteValue::list([]))));
|
assert_eq!(run(source), Ok(Some(ConcreteValue::list([]))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -39,10 +36,7 @@ fn list() {
|
|||||||
FunctionType {
|
FunctionType {
|
||||||
type_parameters: None,
|
type_parameters: None,
|
||||||
value_parameters: None,
|
value_parameters: None,
|
||||||
return_type: Box::new(Type::List {
|
return_type: Box::new(Type::List(Box::new(Type::Integer))),
|
||||||
item_type: Box::new(Type::Integer),
|
|
||||||
length: 3
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(Instruction::load_constant(0, 0, false), Span(1, 2)),
|
(Instruction::load_constant(0, 0, false), Span(1, 2)),
|
||||||
@ -61,7 +55,7 @@ fn list() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
run_source(source),
|
run(source),
|
||||||
Ok(Some(ConcreteValue::list([
|
Ok(Some(ConcreteValue::list([
|
||||||
ConcreteValue::Integer(1),
|
ConcreteValue::Integer(1),
|
||||||
ConcreteValue::Integer(2),
|
ConcreteValue::Integer(2),
|
||||||
@ -81,26 +75,22 @@ fn list_with_complex_expression() {
|
|||||||
FunctionType {
|
FunctionType {
|
||||||
type_parameters: None,
|
type_parameters: None,
|
||||||
value_parameters: None,
|
value_parameters: None,
|
||||||
return_type: Box::new(Type::List {
|
return_type: Box::new(Type::List(Box::new(Type::Integer))),
|
||||||
item_type: Box::new(Type::Integer),
|
|
||||||
length: 3
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(Instruction::load_constant(0, 0, false), Span(1, 2)),
|
(Instruction::load_constant(0, 0, false), Span(1, 2)),
|
||||||
(
|
(
|
||||||
*Instruction::add(1, 1, 2)
|
Instruction::add(1, Argument::Constant(1), Argument::Constant(2)),
|
||||||
.set_b_is_constant()
|
|
||||||
.set_c_is_constant(),
|
|
||||||
Span(6, 7)
|
Span(6, 7)
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
*Instruction::multiply(2, 3, 4)
|
Instruction::multiply(2, Argument::Constant(3), Argument::Constant(4)),
|
||||||
.set_b_is_constant()
|
|
||||||
.set_c_is_constant(),
|
|
||||||
Span(14, 15)
|
Span(14, 15)
|
||||||
),
|
),
|
||||||
(Instruction::subtract(3, 1, 2), Span(10, 11)),
|
(
|
||||||
|
Instruction::subtract(3, Argument::Register(1), Argument::Register(2)),
|
||||||
|
Span(10, 11)
|
||||||
|
),
|
||||||
(Instruction::close(1, 3), Span(17, 18)),
|
(Instruction::close(1, 3), Span(17, 18)),
|
||||||
(Instruction::load_list(4, 0), Span(0, 18)),
|
(Instruction::load_list(4, 0), Span(0, 18)),
|
||||||
(Instruction::r#return(true), Span(18, 18)),
|
(Instruction::r#return(true), Span(18, 18)),
|
||||||
@ -117,7 +107,7 @@ fn list_with_complex_expression() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
run_source(source),
|
run(source),
|
||||||
Ok(Some(ConcreteValue::list([
|
Ok(Some(ConcreteValue::list([
|
||||||
ConcreteValue::Integer(1),
|
ConcreteValue::Integer(1),
|
||||||
ConcreteValue::Integer(-15)
|
ConcreteValue::Integer(-15)
|
||||||
@ -136,17 +126,12 @@ fn list_with_simple_expression() {
|
|||||||
FunctionType {
|
FunctionType {
|
||||||
type_parameters: None,
|
type_parameters: None,
|
||||||
value_parameters: None,
|
value_parameters: None,
|
||||||
return_type: Box::new(Type::List {
|
return_type: Box::new(Type::List(Box::new(Type::Integer))),
|
||||||
item_type: Box::new(Type::Integer),
|
|
||||||
length: 3
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(Instruction::load_constant(0, 0, false), Span(1, 2)),
|
(Instruction::load_constant(0, 0, false), Span(1, 2)),
|
||||||
(
|
(
|
||||||
*Instruction::add(1, 1, 2)
|
Instruction::add(1, Argument::Constant(1), Argument::Constant(2)),
|
||||||
.set_b_is_constant()
|
|
||||||
.set_c_is_constant(),
|
|
||||||
Span(6, 7)
|
Span(6, 7)
|
||||||
),
|
),
|
||||||
(Instruction::load_constant(2, 3, false), Span(11, 12)),
|
(Instruction::load_constant(2, 3, false), Span(11, 12)),
|
||||||
@ -164,7 +149,7 @@ fn list_with_simple_expression() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
run_source(source),
|
run(source),
|
||||||
Ok(Some(ConcreteValue::list([
|
Ok(Some(ConcreteValue::list([
|
||||||
ConcreteValue::Integer(1),
|
ConcreteValue::Integer(1),
|
||||||
ConcreteValue::Integer(5),
|
ConcreteValue::Integer(5),
|
||||||
|
@ -15,7 +15,7 @@ fn and() {
|
|||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(Instruction::load_boolean(0, true, false), Span(0, 4)),
|
(Instruction::load_boolean(0, true, false), Span(0, 4)),
|
||||||
(Instruction::test(0, true), Span(5, 7)),
|
(Instruction::test(Argument::Register(0), true), Span(5, 7)),
|
||||||
(Instruction::jump(1, true), Span(5, 7)),
|
(Instruction::jump(1, true), Span(5, 7)),
|
||||||
(Instruction::load_boolean(1, false, false), Span(8, 13)),
|
(Instruction::load_boolean(1, false, false), Span(8, 13)),
|
||||||
(Instruction::r#return(true), Span(13, 13)),
|
(Instruction::r#return(true), Span(13, 13)),
|
||||||
@ -25,7 +25,7 @@ fn and() {
|
|||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run_source(source), Ok(Some(ConcreteValue::Boolean(false))));
|
assert_eq!(run(source), Ok(Some(ConcreteValue::Boolean(false))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -43,7 +43,7 @@ fn or() {
|
|||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(Instruction::load_boolean(0, true, false), Span(0, 4)),
|
(Instruction::load_boolean(0, true, false), Span(0, 4)),
|
||||||
(Instruction::test(0, false), Span(5, 7)),
|
(Instruction::test(Argument::Register(0), false), Span(5, 7)),
|
||||||
(Instruction::jump(1, true), Span(5, 7)),
|
(Instruction::jump(1, true), Span(5, 7)),
|
||||||
(Instruction::load_boolean(1, false, false), Span(8, 13)),
|
(Instruction::load_boolean(1, false, false), Span(8, 13)),
|
||||||
(Instruction::r#return(true), Span(13, 13)),
|
(Instruction::r#return(true), Span(13, 13)),
|
||||||
@ -53,7 +53,7 @@ fn or() {
|
|||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run_source(source), Ok(Some(ConcreteValue::Boolean(true))));
|
assert_eq!(run(source), Ok(Some(ConcreteValue::Boolean(true))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -75,7 +75,7 @@ fn variable_and() {
|
|||||||
(Instruction::load_boolean(1, false, false), Span(22, 27)),
|
(Instruction::load_boolean(1, false, false), Span(22, 27)),
|
||||||
(Instruction::define_local(1, 1, false), Span(18, 19)),
|
(Instruction::define_local(1, 1, false), Span(18, 19)),
|
||||||
(Instruction::get_local(2, 0), Span(29, 30)),
|
(Instruction::get_local(2, 0), Span(29, 30)),
|
||||||
(Instruction::test(2, true), Span(31, 33)),
|
(Instruction::test(Argument::Register(2), true), Span(31, 33)),
|
||||||
(Instruction::jump(1, true), Span(31, 33)),
|
(Instruction::jump(1, true), Span(31, 33)),
|
||||||
(Instruction::get_local(3, 1), Span(34, 35)),
|
(Instruction::get_local(3, 1), Span(34, 35)),
|
||||||
(Instruction::r#return(true), Span(35, 35)),
|
(Instruction::r#return(true), Span(35, 35)),
|
||||||
@ -88,5 +88,5 @@ fn variable_and() {
|
|||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run_source(source), Ok(Some(ConcreteValue::Boolean(false))));
|
assert_eq!(run(source), Ok(Some(ConcreteValue::Boolean(false))));
|
||||||
}
|
}
|
||||||
|
@ -17,11 +17,14 @@ fn r#while() {
|
|||||||
(Instruction::load_constant(0, 0, false), Span(12, 13)),
|
(Instruction::load_constant(0, 0, false), Span(12, 13)),
|
||||||
(Instruction::define_local(0, 0, true), Span(8, 9)),
|
(Instruction::define_local(0, 0, true), Span(8, 9)),
|
||||||
(
|
(
|
||||||
*Instruction::less(true, 0, 2).set_c_is_constant(),
|
Instruction::less(true, Argument::Register(0), Argument::Constant(2)),
|
||||||
Span(23, 24)
|
Span(23, 24)
|
||||||
),
|
),
|
||||||
(Instruction::jump(2, true), Span(41, 42)),
|
(Instruction::jump(2, true), Span(41, 42)),
|
||||||
(*Instruction::add(0, 0, 3).set_c_is_constant(), Span(35, 36)),
|
(
|
||||||
|
Instruction::add(0, Argument::Register(0), Argument::Constant(3)),
|
||||||
|
Span(35, 36)
|
||||||
|
),
|
||||||
(Instruction::jump(3, false), Span(41, 42)),
|
(Instruction::jump(3, false), Span(41, 42)),
|
||||||
(Instruction::get_local(1, 0), Span(41, 42)),
|
(Instruction::get_local(1, 0), Span(41, 42)),
|
||||||
(Instruction::r#return(true), Span(42, 42)),
|
(Instruction::r#return(true), Span(42, 42)),
|
||||||
@ -36,5 +39,5 @@ fn r#while() {
|
|||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run_source(source), Ok(Some(ConcreteValue::Integer(5))));
|
assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(5))));
|
||||||
}
|
}
|
||||||
|
@ -15,9 +15,7 @@ fn add() {
|
|||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
*Instruction::add(0, 0, 1)
|
Instruction::add(0, Argument::Constant(0), Argument::Constant(1)),
|
||||||
.set_b_is_constant()
|
|
||||||
.set_c_is_constant(),
|
|
||||||
Span(2, 3)
|
Span(2, 3)
|
||||||
),
|
),
|
||||||
(Instruction::r#return(true), Span(5, 5))
|
(Instruction::r#return(true), Span(5, 5))
|
||||||
@ -27,7 +25,7 @@ fn add() {
|
|||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run_source(source), Ok(Some(ConcreteValue::Integer(3))));
|
assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(3))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -46,7 +44,10 @@ fn add_assign() {
|
|||||||
vec![
|
vec![
|
||||||
(Instruction::load_constant(0, 0, false), Span(12, 13)),
|
(Instruction::load_constant(0, 0, false), Span(12, 13)),
|
||||||
(Instruction::define_local(0, 0, true), Span(8, 9)),
|
(Instruction::define_local(0, 0, true), Span(8, 9)),
|
||||||
(*Instruction::add(0, 0, 2).set_c_is_constant(), Span(17, 19)),
|
(
|
||||||
|
Instruction::add(0, Argument::Register(0), Argument::Constant(2)),
|
||||||
|
Span(17, 19)
|
||||||
|
),
|
||||||
(Instruction::get_local(1, 0), Span(23, 24)),
|
(Instruction::get_local(1, 0), Span(23, 24)),
|
||||||
(Instruction::r#return(true), Span(24, 24))
|
(Instruction::r#return(true), Span(24, 24))
|
||||||
],
|
],
|
||||||
@ -59,7 +60,7 @@ fn add_assign() {
|
|||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run_source(source), Ok(Some(ConcreteValue::Integer(3))));
|
assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(3))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -109,9 +110,7 @@ fn divide() {
|
|||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
*Instruction::divide(0, 0, 0)
|
Instruction::divide(0, Argument::Constant(0), Argument::Constant(0)),
|
||||||
.set_b_is_constant()
|
|
||||||
.set_c_is_constant(),
|
|
||||||
Span(2, 3)
|
Span(2, 3)
|
||||||
),
|
),
|
||||||
(Instruction::r#return(true), Span(5, 5))
|
(Instruction::r#return(true), Span(5, 5))
|
||||||
@ -121,7 +120,7 @@ fn divide() {
|
|||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run_source(source), Ok(Some(ConcreteValue::Integer(1))));
|
assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(1))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -141,7 +140,7 @@ fn divide_assign() {
|
|||||||
(Instruction::load_constant(0, 0, false), Span(12, 13)),
|
(Instruction::load_constant(0, 0, false), Span(12, 13)),
|
||||||
(Instruction::define_local(0, 0, true), Span(8, 9)),
|
(Instruction::define_local(0, 0, true), Span(8, 9)),
|
||||||
(
|
(
|
||||||
*Instruction::divide(0, 0, 0).set_c_is_constant(),
|
Instruction::divide(0, Argument::Register(0), Argument::Constant(0)),
|
||||||
Span(17, 19)
|
Span(17, 19)
|
||||||
),
|
),
|
||||||
(Instruction::get_local(1, 0), Span(23, 24)),
|
(Instruction::get_local(1, 0), Span(23, 24)),
|
||||||
@ -152,7 +151,7 @@ fn divide_assign() {
|
|||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run_source(source), Ok(Some(ConcreteValue::Integer(1))));
|
assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(1))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -186,22 +185,21 @@ fn math_operator_precedence() {
|
|||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
*Instruction::add(0, 0, 1)
|
Instruction::add(0, Argument::Constant(0), Argument::Constant(1)),
|
||||||
.set_b_is_constant()
|
|
||||||
.set_c_is_constant(),
|
|
||||||
Span(2, 3)
|
Span(2, 3)
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
*Instruction::multiply(1, 2, 3)
|
Instruction::multiply(1, Argument::Constant(2), Argument::Constant(3)),
|
||||||
.set_b_is_constant()
|
|
||||||
.set_c_is_constant(),
|
|
||||||
Span(10, 11)
|
Span(10, 11)
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
*Instruction::divide(2, 1, 4).set_c_is_constant(),
|
Instruction::divide(2, Argument::Register(1), Argument::Constant(4)),
|
||||||
Span(14, 15)
|
Span(14, 15)
|
||||||
),
|
),
|
||||||
(Instruction::subtract(3, 0, 2), Span(6, 7)),
|
(
|
||||||
|
Instruction::subtract(3, Argument::Register(0), Argument::Register(2)),
|
||||||
|
Span(6, 7)
|
||||||
|
),
|
||||||
(Instruction::r#return(true), Span(17, 17)),
|
(Instruction::r#return(true), Span(17, 17)),
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
@ -215,7 +213,7 @@ fn math_operator_precedence() {
|
|||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run_source(source), Ok(Some(ConcreteValue::Integer(1))));
|
assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(1))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -233,9 +231,7 @@ fn multiply() {
|
|||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
*Instruction::multiply(0, 0, 1)
|
Instruction::multiply(0, Argument::Constant(0), Argument::Constant(1)),
|
||||||
.set_b_is_constant()
|
|
||||||
.set_c_is_constant(),
|
|
||||||
Span(2, 3)
|
Span(2, 3)
|
||||||
),
|
),
|
||||||
(Instruction::r#return(true), Span(5, 5)),
|
(Instruction::r#return(true), Span(5, 5)),
|
||||||
@ -245,7 +241,7 @@ fn multiply() {
|
|||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run_source(source), Ok(Some(ConcreteValue::Integer(2))));
|
assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(2))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -265,7 +261,7 @@ fn multiply_assign() {
|
|||||||
(Instruction::load_constant(0, 0, false), Span(12, 13)),
|
(Instruction::load_constant(0, 0, false), Span(12, 13)),
|
||||||
(Instruction::define_local(0, 0, true), Span(8, 9)),
|
(Instruction::define_local(0, 0, true), Span(8, 9)),
|
||||||
(
|
(
|
||||||
*Instruction::multiply(0, 0, 2).set_c_is_constant(),
|
Instruction::multiply(0, Argument::Register(0), Argument::Constant(2)),
|
||||||
Span(17, 19)
|
Span(17, 19)
|
||||||
),
|
),
|
||||||
(Instruction::get_local(1, 0), Span(22, 23)),
|
(Instruction::get_local(1, 0), Span(22, 23)),
|
||||||
@ -280,7 +276,7 @@ fn multiply_assign() {
|
|||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run_source(source), Ok(Some(ConcreteValue::Integer(6))));
|
assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(6))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -314,9 +310,7 @@ fn subtract() {
|
|||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(
|
(
|
||||||
*Instruction::subtract(0, 0, 1)
|
Instruction::subtract(0, Argument::Constant(0), Argument::Constant(1)),
|
||||||
.set_b_is_constant()
|
|
||||||
.set_c_is_constant(),
|
|
||||||
Span(2, 3)
|
Span(2, 3)
|
||||||
),
|
),
|
||||||
(Instruction::r#return(true), Span(5, 5)),
|
(Instruction::r#return(true), Span(5, 5)),
|
||||||
@ -326,7 +320,7 @@ fn subtract() {
|
|||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run_source(source), Ok(Some(ConcreteValue::Integer(-1))));
|
assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(-1))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -346,7 +340,7 @@ fn subtract_assign() {
|
|||||||
(Instruction::load_constant(0, 0, false), Span(12, 14)),
|
(Instruction::load_constant(0, 0, false), Span(12, 14)),
|
||||||
(Instruction::define_local(0, 0, true), Span(8, 9)),
|
(Instruction::define_local(0, 0, true), Span(8, 9)),
|
||||||
(
|
(
|
||||||
*Instruction::subtract(0, 0, 2).set_c_is_constant(),
|
Instruction::subtract(0, Argument::Register(0), Argument::Constant(2)),
|
||||||
Span(18, 20)
|
Span(18, 20)
|
||||||
),
|
),
|
||||||
(Instruction::get_local(1, 0), Span(24, 25)),
|
(Instruction::get_local(1, 0), Span(24, 25)),
|
||||||
@ -361,7 +355,7 @@ fn subtract_assign() {
|
|||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run_source(source), Ok(Some(ConcreteValue::Integer(40))));
|
assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(40))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -31,7 +31,7 @@ fn panic() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
run_source(source),
|
run(source),
|
||||||
Err(DustError::Runtime {
|
Err(DustError::Runtime {
|
||||||
error: VmError::NativeFunction(NativeFunctionError::Panic {
|
error: VmError::NativeFunction(NativeFunctionError::Panic {
|
||||||
message: Some("Goodbye world! 42".to_string()),
|
message: Some("Goodbye world! 42".to_string()),
|
||||||
@ -53,7 +53,7 @@ fn to_string() {
|
|||||||
FunctionType {
|
FunctionType {
|
||||||
type_parameters: None,
|
type_parameters: None,
|
||||||
value_parameters: None,
|
value_parameters: None,
|
||||||
return_type: Box::new(Type::String { length: None }),
|
return_type: Box::new(Type::String),
|
||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(Instruction::load_constant(0, 0, false), Span(10, 12)),
|
(Instruction::load_constant(0, 0, false), Span(10, 12)),
|
||||||
@ -68,5 +68,5 @@ fn to_string() {
|
|||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run_source(source), Ok(Some(ConcreteValue::string("42"))))
|
assert_eq!(run(source), Ok(Some(ConcreteValue::string("42"))))
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ fn allow_access_to_parent_scope() {
|
|||||||
}
|
}
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
assert_eq!(run_source(source), Ok(Some(ConcreteValue::Integer(1))));
|
assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(1))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -69,7 +69,7 @@ fn block_scope() {
|
|||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run_source(source), Ok(None));
|
assert_eq!(run(source), Ok(None));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -149,7 +149,7 @@ fn multiple_block_scopes() {
|
|||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run_source(source), Ok(None));
|
assert_eq!(run(source), Ok(None));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -162,7 +162,7 @@ fn disallow_access_to_child_scope() {
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
run_source(source),
|
run(source),
|
||||||
Err(DustError::Compile {
|
Err(DustError::Compile {
|
||||||
error: CompileError::VariableOutOfScope {
|
error: CompileError::VariableOutOfScope {
|
||||||
identifier: "x".to_string(),
|
identifier: "x".to_string(),
|
||||||
@ -187,7 +187,7 @@ fn disallow_access_to_child_scope_nested() {
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
run_source(source),
|
run(source),
|
||||||
Err(DustError::Compile {
|
Err(DustError::Compile {
|
||||||
error: CompileError::VariableOutOfScope {
|
error: CompileError::VariableOutOfScope {
|
||||||
identifier: "x".to_string(),
|
identifier: "x".to_string(),
|
||||||
@ -212,7 +212,7 @@ fn disallow_access_to_sibling_scope() {
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
run_source(source),
|
run(source),
|
||||||
Err(DustError::Compile {
|
Err(DustError::Compile {
|
||||||
error: CompileError::VariableOutOfScope {
|
error: CompileError::VariableOutOfScope {
|
||||||
identifier: "x".to_string(),
|
identifier: "x".to_string(),
|
||||||
@ -239,7 +239,7 @@ fn disallow_access_to_sibling_scope_nested() {
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
run_source(source),
|
run(source),
|
||||||
Err(DustError::Compile {
|
Err(DustError::Compile {
|
||||||
error: CompileError::VariableOutOfScope {
|
error: CompileError::VariableOutOfScope {
|
||||||
identifier: "x".to_string(),
|
identifier: "x".to_string(),
|
||||||
|
@ -14,7 +14,7 @@ fn negate() {
|
|||||||
return_type: Box::new(Type::Integer),
|
return_type: Box::new(Type::Integer),
|
||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(*Instruction::negate(0, 0).set_b_is_constant(), Span(0, 1)),
|
(Instruction::negate(0, Argument::Constant(0)), Span(0, 1)),
|
||||||
(Instruction::r#return(true), Span(5, 5)),
|
(Instruction::r#return(true), Span(5, 5)),
|
||||||
],
|
],
|
||||||
vec![ConcreteValue::Integer(42)],
|
vec![ConcreteValue::Integer(42)],
|
||||||
@ -22,7 +22,7 @@ fn negate() {
|
|||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run_source(source), Ok(Some(ConcreteValue::Integer(-42))));
|
assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(-42))));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -40,7 +40,7 @@ fn not() {
|
|||||||
},
|
},
|
||||||
vec![
|
vec![
|
||||||
(Instruction::load_boolean(0, true, false), Span(1, 5)),
|
(Instruction::load_boolean(0, true, false), Span(1, 5)),
|
||||||
(Instruction::not(1, 0), Span(0, 1)),
|
(Instruction::not(1, Argument::Register(0)), Span(0, 1)),
|
||||||
(Instruction::r#return(true), Span(5, 5)),
|
(Instruction::r#return(true), Span(5, 5)),
|
||||||
],
|
],
|
||||||
vec![],
|
vec![],
|
||||||
@ -48,5 +48,5 @@ fn not() {
|
|||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run_source(source), Ok(Some(ConcreteValue::Boolean(false))));
|
assert_eq!(run(source), Ok(Some(ConcreteValue::Boolean(false))));
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ fn define_local() {
|
|||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run_source(source), Ok(None));
|
assert_eq!(run(source), Ok(None));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -73,5 +73,5 @@ fn set_local() {
|
|||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(run_source(source), Ok(Some(ConcreteValue::Integer(42))));
|
assert_eq!(run(source), Ok(Some(ConcreteValue::Integer(42))));
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user