use crate::*;
use assembler::{Symbol, SymbolRole, DefinitionType};
use SymbolRole::*;
use DefinitionType::*;
use std::path::Path;
pub fn new_compiler() -> Compiler {
Compiler::new(parse_symbols, push_code)
}
/// Parse all symbols from a source code string.
pub fn parse_symbols(source_code: &str, path: Option<&Path>) -> Option<Vec<Symbol>> {
let syntactic = match parse_syntactic(source_code, path) {
Ok(syntactic) => syntactic,
Err(_errors) => return None,
};
let semantic = match parse_semantic(syntactic) {
Ok(semantic) => semantic,
Err(_errors) => return None,
};
Some(SymbolParser::new().parse(&semantic))
}
/// Push source code to a source compilation string.
pub fn push_code(compilation: &mut String, source_file: &SourceFile) {
// Skip blank files.
let source_code = &source_file.source_code;
if source_code.chars().all(|c| c.is_whitespace()) { return; }
// Ensure that the previous section is followed by two newline characters.
if !compilation.is_empty() {
if !compilation.ends_with('\n') { compilation.push('\n'); }
if !compilation.ends_with("\n\n") { compilation.push('\n'); }
}
// Push a path comment and the source code.
let path_str = source_file.path.as_os_str().to_string_lossy();
let path_comment = format!("(: {path_str} )\n");
compilation.push_str(&path_comment);
compilation.push_str(&source_code);
}
// Extract symbol definitions from a list of semantic tokens.
pub struct SymbolParser {
pub macro_name: Option<String>,
pub symbols: Vec<Symbol>,
}
impl SymbolParser {
pub fn new() -> Self {
Self {
macro_name: None,
symbols: Vec::new(),
}
}
fn record_symbol(&mut self, name: &str, source: &SourceSpan, role: SymbolRole) {
let name = name.to_string();
let namespace = match &self.macro_name {
Some(macro_name) => vec![macro_name.to_owned()],
None => vec![],
};
let source = source.to_owned();
self.symbols.push(Symbol { name, namespace, source, role });
}
pub fn parse(mut self, semantic: &[Tracked<SemanticToken>]) -> Vec<Symbol> {
for token in semantic {
let source = &token.source;
match &token.value {
SemanticToken::MacroDefinition(definition) => {
// Record macro definition.
self.record_symbol(
&definition.name,
&definition.name.source,
Definition(MustPrecedeReference),
);
self.macro_name = Some(definition.name.to_string());
for argument in &definition.arguments {
self.record_symbol(
&argument.name,
&argument.source,
Definition(MustPrecedeReference),
);
}
match &definition.body {
MacroDefinitionBody::Integer(integer) => {
self.parse_integer_token(&integer, &integer.source)
}
MacroDefinitionBody::Invocation(invocation) => {
self.parse_invocation(&invocation, &invocation.source)
}
MacroDefinitionBody::Block(tokens) => {
for token in tokens {
self.parse_block_token(&token, &token.source);
}
}
}
self.macro_name = None;
}
SemanticToken::BlockToken(token) => {
self.parse_block_token(token, &source);
}
}
}
return self.symbols;
}
fn parse_expression(&mut self, expression: &Expression, _source: &SourceSpan) {
for token in &expression.tokens {
let source = &token.source;
match &token.value {
ExpressionToken::IntegerToken(integer) => {
self.parse_integer_token(integer, source);
}
ExpressionToken::Invocation(invocation) => {
self.parse_invocation(invocation, source);
}
ExpressionToken::Operator(_) => (),
}
}
}
fn parse_invocation(&mut self, invocation: &Invocation, source: &SourceSpan) {
self.record_symbol(
&invocation.name,
&source,
Reference,
);
for argument in &invocation.arguments {
let source = &argument.source;
match &argument.value {
InvocationArgument::IntegerToken(integer) => {
self.parse_integer_token(integer, &source);
}
InvocationArgument::BlockToken(block) => {
self.parse_block_token(block, &source);
}
InvocationArgument::Invocation(invocation) => {
self.parse_invocation(invocation, &source);
}
InvocationArgument::String(_) => (),
}
}
}
fn parse_block_token(&mut self, token: &BlockToken, source: &SourceSpan) {
match token {
BlockToken::LabelDefinition(name) => {
self.record_symbol(
&name,
&source,
Definition(CanFollowReference),
);
}
BlockToken::PinnedAddress(integer) => {
self.parse_integer_token(integer, &integer.source);
}
BlockToken::ConditionalBlock(condition) => {
self.parse_integer_token(&condition.predicate, &condition.predicate.source);
self.parse_block_token(&condition.body, &condition.body.source);
}
BlockToken::WordTemplate(word_template) => {
for field in &word_template.fields {
self.record_symbol(
&field.name.to_string(),
&field.source,
Reference,
);
}
}
BlockToken::Block(tokens) => {
for token in tokens {
self.parse_block_token(token, &token.source);
}
}
BlockToken::Invocation(invocation) => {
self.parse_invocation(invocation, source);
}
}
}
fn parse_integer_token(&mut self, token: &IntegerToken, source: &SourceSpan) {
match &token {
IntegerToken::Expression(expression) => {
self.parse_expression(&expression, source)
}
IntegerToken::Invocation(invocation) => {
self.parse_invocation(&invocation, source)
}
IntegerToken::IntegerLiteral(_) => (),
}
}
}