use std::{ cell::RefCell, collections::HashMap, fmt::{ self, Display, Formatter }, rc::Rc };
use crate::language::{ ast::*,
bytecode::{ Code, Instruction, Function, FunctionBlock, FunctionBlockRef },
data::value::Value,
text::location::Location,
parser::ParserError };
pub enum ErrorWhat
{
ParserError(ParserError),
}
pub struct CompileError
{
pub location: Option<Location>,
pub what: ErrorWhat
}
impl Display for CompileError
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result
{
match &self.what
{
ErrorWhat::ParserError(error) =>
{
if let Some(location) = &self.location
{
write!(f, "Parser error: {}: {}", location, error)
}
else
{
write!(f, "Parser error: {}", error)
}
}
}
}
}
impl From<ParserError> for CompileError
{
fn from(error: ParserError) -> Self
{
CompileError
{
location: None,
what: ErrorWhat::ParserError(error)
}
}
}
pub type CompileResult<T> = Result<T, CompileError>;
fn compile_expression(instructions: &mut Vec<Instruction>, expression: &AstExpression)
{
match &expression.kind
{
AstExpressionKind::Symbol(symbol) =>
{
if symbol.name.contains('*')
{
instructions.push(Instruction
{
location: Some(expression.location.clone()),
code: Code::GlobFiles,
operand: Some(Value::String(symbol.name.clone()))
});
}
else
{
instructions.push(Instruction
{
location: Some(expression.location.clone()),
code: Code::Push,
operand: Some(Value::String(symbol.name.clone()))
});
}
},
AstExpressionKind::Literal(value) =>
{
instructions.push(Instruction
{
location: Some(expression.location.clone()),
code: Code::Push,
operand: Some(value.value.clone())
});
},
AstExpressionKind::Variable(variable) =>
{
instructions.push(Instruction
{
location: Some(expression.location.clone()),
code: Code::GetVariable,
operand: Some(Value::String(variable.name.clone()))
});
}
AstExpressionKind::VariableSplat(variable) =>
{
instructions.push(Instruction
{
location: Some(expression.location.clone()),
code: Code::GetVariable,
operand: Some(Value::String(variable.name.clone()))
});
instructions.push(Instruction
{
location: Some(expression.location.clone()),
code: Code::ExpandArray,
operand: None
});
},
AstExpressionKind::MathExpression(operator, lhs, rhs) =>
{
compile_expression(instructions, lhs);
compile_expression(instructions, rhs);
instructions.push(Instruction
{
location: Some(expression.location.clone()),
code: match operator
{
AstMathOperator::Add => Code::MathAdd,
AstMathOperator::Subtract => Code::MathSubtract,
AstMathOperator::Multiply => Code::MathMultiply,
AstMathOperator::Divide => Code::MathDivide,
AstMathOperator::Modulo => Code::MathModulo,
},
operand: None
});
}
}
if let Some(string_flag) = &expression.string_flag
{
if let AstStringFlag::Interpolated = string_flag
{
instructions.push(Instruction
{
location: Some(expression.location.clone()),
code: Code::InterpolateString,
operand: None
});
}
}
if let AstExpressionKind::Symbol(symbol) = &expression.kind
&& (symbol.name == "~" || symbol.name.starts_with("~/"))
&& !symbol.name.contains('*')
{
instructions.push(Instruction
{
location: Some(expression.location.clone()),
code: Code::ExpandPath,
operand: None
});
}
}
fn compile_let_statement(instructions: &mut Vec<Instruction>, let_statement: &AstLetStatement)
{
instructions.push(Instruction
{
location: Some(let_statement.location.clone()),
code: Code::NewVariable,
operand: Some(Value::String(let_statement.identifier.clone()))
});
if let_statement.export_flag == AstExportFlag::Exported
{
instructions.push(Instruction
{
location: None,
code: Code::ExportVariable,
operand: Some(Value::String(let_statement.identifier.clone()))
});
}
compile_expression(instructions, &let_statement.expression);
instructions.push(Instruction
{
location: Some(let_statement.location.clone()),
code: Code::SetVariable,
operand: Some(Value::String(let_statement.identifier.clone()))
});
}
fn compile_set_statement(instructions: &mut Vec<Instruction>, set_statement: &AstSetStatement)
{
compile_expression(instructions, &set_statement.expression);
instructions.push(Instruction
{
location: Some(set_statement.location.clone()),
code: Code::SetVariable,
operand: Some(Value::String(set_statement.identifier.clone()))
});
}
fn compile_alias_statement(instructions: &mut Vec<Instruction>, alias_statement: &AstAliasStatement)
{
instructions.push(Instruction
{
location: Some(alias_statement.location.clone()),
code: Code::Push,
operand: Some(Value::String(alias_statement.target.clone()))
});
instructions.push(Instruction
{
location: Some(alias_statement.location.clone()),
code: Code::ExpandPath,
operand: None
});
for argument in &alias_statement.arguments
{
instructions.push(Instruction
{
location: Some(alias_statement.location.clone()),
code: Code::Push,
operand: Some(argument.value.clone())
});
if argument.expand_path
{
instructions.push(Instruction
{
location: Some(alias_statement.location.clone()),
code: Code::ExpandPath,
operand: None
});
}
}
instructions.push(Instruction
{
location: Some(alias_statement.location.clone()),
code: Code::NewAlias,
operand: Some(Value::Array(vec![Value::String(alias_statement.alias.clone()),
Value::Integer(alias_statement.arguments.len() as i64)]))
});
}
fn compile_execute_statement(instructions: &mut Vec<Instruction>,
execute_statement: &AstExecuteStatement)
{
instructions.push(Instruction
{
location: None,
code: Code::Push,
operand: Some(Value::String(execute_statement.executable_name.clone()))
});
if execute_statement.expand_path
{
instructions.push(Instruction
{
location: Some(execute_statement.location.clone()),
code: Code::ExpandPath,
operand: None
});
}
for argument in &execute_statement.arguments
{
compile_expression(instructions, argument);
}
instructions.push(Instruction
{
location: Some(execute_statement.location.clone()),
code: Code::Execute,
operand: Some(Value::Integer(execute_statement.arguments.len() as i64))
});
}
fn compile_function_definition(parent_block: &FunctionBlockRef,
function_statement: &AstFunctionStatement) -> CompileResult<()>
{
let function_block = Rc::new(RefCell::new(FunctionBlock
{
parent: Some(parent_block.clone()),
functions: HashMap::new()
}));
let instructions = compile_ast(&function_block, &function_statement.body)?;
let new_function = Rc::new(Function
{
functions: function_block,
arguments: function_statement.parameters.clone(),
code: instructions
});
parent_block.borrow_mut().functions.insert(function_statement.name.clone(), new_function);
Ok(())
}
pub fn compile_ast(function_block: &FunctionBlockRef,
ast: &AstTopLevel) -> CompileResult<Vec<Instruction>>
{
let mut instructions = Vec::new();
for ast_item in ast
{
match ast_item
{
AstStatement::NullStatement => {},
AstStatement::LetStatement(let_statement) =>
{
compile_let_statement(&mut instructions, let_statement);
},
AstStatement::SetStatement(set_statement) =>
{
compile_set_statement(&mut instructions, set_statement);
},
AstStatement::AliasStatement(alias_statement) =>
{
compile_alias_statement(&mut instructions, alias_statement);
},
AstStatement::ExecuteStatement(execute_statement) =>
{
compile_execute_statement(&mut instructions, execute_statement);
}
AstStatement::FunctionDefinition(function_statement) =>
{
compile_function_definition(&function_block, function_statement)?;
}
}
}
Ok(instructions)
}