diff --git a/.gitignore b/.gitignore index 32fc6a5..a0b9c11 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ /target - +*.log +*.reidc #Added by cargo # diff --git a/reid_src/test.reid b/reid_src/test.reid index 971ff3f..cdbddb0 100644 --- a/reid_src/test.reid +++ b/reid_src/test.reid @@ -1 +1,2 @@ -let gotus = 3; \ No newline at end of file +let gotus = "Hello, world!"; +print(gotus); \ No newline at end of file diff --git a/src/args.rs b/src/args.rs index 65bebf4..66bfc99 100644 --- a/src/args.rs +++ b/src/args.rs @@ -5,8 +5,10 @@ use argh::FromArgs; #[derive(FromArgs, PartialEq, Debug)] #[argh(description = "reid compiler and Virtual Machine")] pub struct MainOpt { + #[argh(positional, description = "run compiled .reidc from ")] + pub run_path: Option, #[argh(subcommand)] - pub subcommand: Subcommand, + pub subcommand: Option, } #[derive(FromArgs, PartialEq, Debug)] @@ -14,7 +16,6 @@ pub struct MainOpt { pub enum Subcommand { Compile(Compile), Run(Run), - CompileAndRun(CompileAndRun), } #[derive(FromArgs, PartialEq, Debug)] @@ -27,27 +28,16 @@ pub struct Compile { #[argh(positional, description = "source .reid path")] pub source: String, #[argh(positional, description = "output .reidc path")] - pub output: String, + pub output: Option, } #[derive(FromArgs, PartialEq, Debug)] #[argh( subcommand, name = "run", - description = "run compiled .reidc from " -)] -pub struct Run { - #[argh(positional, description = "otus 2")] - pub path: String, -} - -#[derive(FromArgs, PartialEq, Debug)] -#[argh( - subcommand, - name = "c_run", description = "compile and run given .reid file" )] -pub struct CompileAndRun { +pub struct Run { #[argh(positional, description = "source .reid path")] pub source: PathBuf, } diff --git a/src/compiler.rs b/src/compiler.rs index 2ec1758..361bf50 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -7,9 +7,9 @@ use super::parser::{Expression, LiteralPattern, ParsedReid, Pattern, Position, S use super::vm::{FunctionSignature, VariableType}; type Variable = (HeapID, VariableType); -pub type FuncID = usize; -pub type HeapID = usize; -pub type RegID = usize; +pub type FuncID = u16; +pub type HeapID = u16; +pub type RegID = u8; #[derive(Debug, Clone)] pub enum Command { @@ -192,11 +192,11 @@ impl Scope { } } - fn find_function(&self, signature: &FunctionSignature) -> Option<(usize, &FunctionSignature)> { + fn find_function(&self, signature: &FunctionSignature) -> Option<(FuncID, &FunctionSignature)> { let mut found = None; for (idx, func) in self.functions.iter().enumerate() { if func == signature { - found = Some((idx, func)); + found = Some((idx as u16, func)); } } found diff --git a/src/errors.rs b/src/errors.rs index 79762e4..28a543c 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -9,6 +9,9 @@ use super::vm::VariableType; #[derive(Debug)] pub enum GenericError { StdIOError(io::Error), + CorruptedBytecode, + SyntaxError(SyntaxError), + CompilerError(CompilerError), } impl From for GenericError { @@ -17,6 +20,32 @@ impl From for GenericError { } } +impl From for GenericError { + fn from(error: SyntaxError) -> Self { + Self::SyntaxError(error) + } +} + +impl From for GenericError { + fn from(error: CompilerError) -> Self { + Self::CompilerError(error) + } +} + +impl Display for GenericError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let text = match self { + GenericError::StdIOError(err) => format!("IO Error: {}", err), + GenericError::CorruptedBytecode => { + "Failed to read bytecode. Bytecode might be corrupted.".to_string() + } + GenericError::SyntaxError(err) => format!("Syntax Error: {}", err), + GenericError::CompilerError(err) => format!("Compiler Error: {}", err), + }; + write!(f, "{}", text) + } +} + #[derive(Debug)] pub enum SyntaxError { #[allow(dead_code)] diff --git a/src/file_io.rs b/src/file_io.rs index e1b98d9..cabbd40 100644 --- a/src/file_io.rs +++ b/src/file_io.rs @@ -3,12 +3,165 @@ use std::io::prelude::*; use std::io::BufReader; use std::path::Path; +use super::compiler::{Command, CompiledReid}; use super::errors::GenericError; +use super::vm::VariableType; -pub fn open_file(path: &Path) -> Result { +pub fn open_source(path: &Path) -> Result { let file = File::open(path)?; let mut reader = BufReader::new(file); let mut text = String::new(); reader.read_to_string(&mut text)?; Ok(text) } + +pub fn into_bytecode(compiled: &CompiledReid) -> Vec { + let mut list = Vec::new(); + let iter = compiled.list.iter(); + for item in iter { + list.append(&mut item.into_u8()); + } + list +} + +pub fn write_bytecode(bytecode: Vec, path: &Path) -> Result<(), GenericError> { + let mut file = File::create(path)?; + file.write_all(&bytecode)?; + Ok(()) +} + +pub fn open_bytecode(path: &Path) -> Result { + let file = File::open(path)?; + let mut reader = BufReader::new(file); + let mut commands = Vec::new(); + let mut iter = reader.fill_buf()?.iter().peekable(); + while iter.peek().is_some() { + if let Some(command) = Command::from_u8(&mut iter) { + commands.push(command); + } else { + return Err(GenericError::CorruptedBytecode); + } + } + Ok(CompiledReid { list: commands }) +} + +impl VariableType { + fn into_u8(&self) -> u8 { + match self { + VariableType::TypeString => 0, + VariableType::TypeI32 => 1, + } + } + + fn from_u8(num: u8) -> Option { + match num { + 0 => Some(VariableType::TypeString), + 1 => Some(VariableType::TypeI32), + _ => None, + } + } +} + +impl Command { + fn id(&self) -> u8 { + match *self { + Command::InitializeVariable(..) => 0, + Command::BeginScope => 1, + Command::EndScope => 2, + Command::Pop(..) => 3, + Command::Push(..) => 4, + Command::AssignVariable(..) => 5, + Command::VarToReg(..) => 6, + Command::StringLit(..) => 7, + Command::I32Lit(..) => 8, + Command::FunctionCall(..) => 9, + } + } + + fn from_u8<'a, T: Iterator>(iter: &mut T) -> Option { + let id = iter.next()?; + match id { + 0 => { + let heapid = u16::from_be_bytes([*iter.next()?, *iter.next()?]); + Some(Command::InitializeVariable( + heapid, + VariableType::from_u8(*iter.next()?)?, + )) + } + 1 => Some(Command::BeginScope), + 2 => Some(Command::EndScope), + 3 => Some(Command::Pop(*iter.next()?)), + 4 => Some(Command::Push(*iter.next()?)), + 5 => { + let heapid = u16::from_be_bytes([*iter.next()?, *iter.next()?]); + Some(Command::AssignVariable(heapid, *iter.next()?)) + } + 6 => { + let heapid = u16::from_be_bytes([*iter.next()?, *iter.next()?]); + Some(Command::VarToReg(heapid, *iter.next()?)) + } + 7 => { + let len = u32::from_be_bytes([ + *iter.next()?, + *iter.next()?, + *iter.next()?, + *iter.next()?, + ]); + let string = String::from_utf8(iter.take(len as usize).cloned().collect()).ok()?; + Some(Command::StringLit(string)) + } + 8 => { + let num = i32::from_be_bytes([ + *iter.next()?, + *iter.next()?, + *iter.next()?, + *iter.next()?, + ]); + Some(Command::I32Lit(num)) + } + 9 => { + let funcid = u16::from_be_bytes([*iter.next()?, *iter.next()?]); + Some(Command::FunctionCall(funcid)) + } + _ => None, + } + } + + fn into_u8(&self) -> Vec { + let mut list = Vec::new(); + list.push(self.id()); + match &self { + Command::InitializeVariable(heapid, variabletype) => { + let heapid = heapid.to_be_bytes(); + list.append(&mut vec![heapid[0], heapid[1], variabletype.into_u8()]) + } + Command::BeginScope => (), + Command::EndScope => (), + Command::Pop(regid) => list.push(*regid), + Command::Push(regid) => list.push(*regid), + Command::AssignVariable(heapid, regid) => { + let heapid = heapid.to_be_bytes(); + list.append(&mut vec![heapid[0], heapid[1], *regid]); + } + Command::VarToReg(heapid, regid) => { + let heapid = heapid.to_be_bytes(); + list.append(&mut vec![heapid[0], heapid[1], *regid]); + } + Command::StringLit(string) => { + let string = string.as_bytes(); + let len = (string.len() as u32).to_be_bytes(); + list.append(&mut vec![len[0], len[1], len[2], len[3]]); + list.append(&mut string.to_vec()); + } + Command::I32Lit(num) => { + let num = num.to_be_bytes(); + list.append(&mut vec![num[0], num[1], num[2], num[3]]); + } + Command::FunctionCall(funcid) => { + let funcid = funcid.to_be_bytes(); + list.append(&mut vec![funcid[0], funcid[1]]); + } + } + list + } +} diff --git a/src/main.rs b/src/main.rs index ad1802b..0a000a7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,11 +8,13 @@ mod file_io; mod parser; mod vm; -use file_io::open_file; -use std::path::Path; +use file_io::{into_bytecode, open_bytecode, open_source, write_bytecode}; +use std::env; +use std::path::{Path, PathBuf}; use args::*; use compiler::{CompiledReid, Compiler}; +use errors::GenericError; use parser::Parser; use vm::{ BuiltinFunctionDef, BuiltinFunctions, FunctionSignature, Value, VariableType, VirtualMachine, @@ -33,35 +35,69 @@ fn main() { let opt: MainOpt = argh::from_env(); - match opt.subcommand { - Subcommand::Compile(_) => {} - Subcommand::Run(_) => {} - Subcommand::CompileAndRun(c_run) => { - let path = Path::new(&c_run.source); - let compiled = compile(path, builtin_functions.signatures()); - run(compiled, builtin_functions); + if let Some(run_path) = opt.run_path { + let compiled = open_bytecode(&run_path); + match compiled { + Ok(compiled) => run(compiled, builtin_functions), + Err(error) => { + eprintln!("{}", error); + std::process::exit(1); + } } + } else if let Some(subcommand) = opt.subcommand { + match subcommand { + Subcommand::Compile(opt) => { + let source = Path::new(&opt.source); + let output = if let Some(output) = opt.output { + PathBuf::from(output) + } else { + source.with_extension("reidc") + }; + let compiled = compile(source, builtin_functions.signatures()); + match compiled { + Ok(compiled) => { + let bytecode = into_bytecode(&compiled); + if let Err(err) = write_bytecode(bytecode, &output) { + eprintln!("{}", err); + std::process::exit(1); + } + } + Err(error) => { + eprintln!("{}", error); + std::process::exit(1); + } + } + } + Subcommand::Run(opt) => { + let path = Path::new(&opt.source); + let compiled = compile(path, builtin_functions.signatures()); + match compiled { + Ok(compiled) => run(compiled, builtin_functions), + Err(error) => { + eprintln!("{}", error); + std::process::exit(1); + } + } + } + } + } else { + let command = env::args().collect::>().join(" "); + eprintln!("Please try running instead:"); + eprintln!(" {} ", command); + eprintln!(" {} help", command); } } -fn compile(path: &Path, builtin: Vec) -> CompiledReid { - let parsed = Parser::from(open_file(&path).ok().unwrap()).parse(); - if let Err(error) = parsed { - eprintln!("Syntax error: {}", error); - std::process::exit(1); - } - dbg!(&parsed); +fn compile(path: &Path, builtin: Vec) -> Result { + let parsed = Parser::from(open_source(&path)?).parse()?; + //dbg!(&parsed); - let compiled = Compiler::from(parsed.unwrap()) + let compiled = Compiler::from(parsed) .with_builtin_functions(builtin) - .compile(); - if let Err(error) = compiled { - eprintln!("Compilation error: {}", error); - std::process::exit(1); - } - dbg!(&compiled); + .compile()?; + //dbg!(&compiled); - compiled.unwrap() + Ok(compiled) } fn run(reid: CompiledReid, builtin: BuiltinFunctions) { diff --git a/src/vm/mod.rs b/src/vm/mod.rs index 3e79ba5..9bbc95a 100644 --- a/src/vm/mod.rs +++ b/src/vm/mod.rs @@ -92,7 +92,7 @@ impl VirtualMachine { } Command::Pop(regid) => { if let Some(val) = self.stack.pop() { - self.registry[regid] = Some(val); + self.registry[regid as usize] = Some(val); //dbg!("Registry popped", regid, &self.stack, &self.registry); Ok(()) } else { @@ -100,7 +100,7 @@ impl VirtualMachine { } } Command::Push(regid) => { - if let Some(reg) = &self.registry[regid] { + if let Some(reg) = &self.registry[regid as usize] { if self.stack.len() < usize::MAX { self.stack.push(reg.clone()); //dbg!("Registry pushed", regid, &self.stack); @@ -113,7 +113,7 @@ impl VirtualMachine { } } Command::AssignVariable(heapid, regid) => { - if let Some(reg) = &self.registry[regid] { + if let Some(reg) = &self.registry[regid as usize] { if let Some(var) = self.heap.get_mut(&heapid) { var.try_set(Some(reg.clone()))?; //dbg!("Variable assigned", heapid, regid, &self.heap); @@ -128,7 +128,7 @@ impl VirtualMachine { Command::VarToReg(heapid, regid) => { if let Some(var) = self.heap.get(&heapid) { if let Some(val) = &var.1 { - self.registry[regid] = Some(val.clone()); + self.registry[regid as usize] = Some(val.clone()); //dbg!("Variable pushed to registry", heapid, regid, &self.registry); Ok(()) } else { @@ -156,10 +156,10 @@ impl VirtualMachine { } } Command::FunctionCall(funcid) => { - if self.functions.len() <= funcid { + if self.functions.len() <= funcid as usize { Err(RuntimePanic::InvalidFuncAddress) } else { - match &self.functions[funcid] { + match &self.functions[funcid as usize] { FunctionDef::Builtin(f) => { let mut params = Vec::new(); for _ in 0..f.signature.parameters.len() {