summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Bridle <ben@derelict.engineering>2025-02-27 14:53:21 +1300
committerBen Bridle <ben@derelict.engineering>2025-02-27 14:53:31 +1300
commit67470aea034fd46f4bbcfe815c51ad3451043188 (patch)
tree83d78d3d28e094d6a3af347d2ff2ff16472e5421
parent4e8fae09f0f7d6f3a4ddbe285aeb01ef0622b761 (diff)
downloadtorque-asm-67470aea034fd46f4bbcfe815c51ad3451043188.zip
Finish first working version of Torque
This is a huge and messy commit, worked on piecemeal while traveling and while the language was still being designed.
-rw-r--r--Cargo.lock26
-rw-r--r--Cargo.toml11
-rw-r--r--src/compiler.rs24
-rw-r--r--src/environment.rs56
-rw-r--r--src/expression_evaluator.rs0
-rw-r--r--src/formats/inhx32.rs87
-rw-r--r--src/formats/mod.rs2
-rw-r--r--src/main.rs201
-rw-r--r--src/parsers/assembler.rs282
-rw-r--r--src/parsers/bytecode.rs161
-rw-r--r--src/parsers/expression.rs (renamed from src/parsers/constant_expression.rs)26
-rw-r--r--src/parsers/mod.rs14
-rw-r--r--src/parsers/packed_binary_literal.rs43
-rw-r--r--src/parsers/semantic.rs339
-rw-r--r--src/parsers/syntactic.rs197
-rw-r--r--src/report.rs229
-rw-r--r--src/tokens/assembler.rs140
-rw-r--r--src/tokens/bytecode.rs49
-rw-r--r--src/tokens/constant_expression.rs134
-rw-r--r--src/tokens/expression.rs74
-rw-r--r--src/tokens/mod.rs22
-rw-r--r--src/tokens/packed_binary_literal.rs11
-rw-r--r--src/tokens/semantic.rs184
-rw-r--r--src/tokens/syntactic.rs32
-rw-r--r--src/tokens/tracked.rs47
-rw-r--r--src/tokens/tracked_integer.rs14
26 files changed, 1673 insertions, 732 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 01ff6b3..c99f391 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -9,7 +9,8 @@ source = "git+git://benbridle.com/ansi?tag=v1.0.0#81d47867c2c97a9ae1d1c8fdfcd42c
[[package]]
name = "assembler"
-version = "1.1.0"
+version = "2.0.0"
+source = "git+git://benbridle.com/assembler?tag=v2.0.0#0b4a82bf73339079490e9643cb115dbf82f3e92b"
dependencies = [
"ansi",
"log 1.1.2",
@@ -17,6 +18,28 @@ dependencies = [
]
[[package]]
+name = "equivalent"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
+
+[[package]]
+name = "hashbrown"
+version = "0.15.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
+
+[[package]]
+name = "indexmap"
+version = "2.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652"
+dependencies = [
+ "equivalent",
+ "hashbrown",
+]
+
+[[package]]
name = "log"
version = "1.1.1"
source = "git+git://benbridle.com/log?tag=v1.1.1#930f3d0e2b82df1243f423c092a38546ea7533c3"
@@ -49,6 +72,7 @@ name = "torque-asm"
version = "0.1.0"
dependencies = [
"assembler",
+ "indexmap",
"log 1.1.2",
"paste",
"switchboard",
diff --git a/Cargo.toml b/Cargo.toml
index e62f942..be881f2 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -4,9 +4,16 @@ version = "0.1.0"
edition = "2021"
[dependencies]
-assembler = { path = "/home/ben/Libraries/assembler" }
-# assembler = { git = "git://benbridle.com/assembler", tag = "v1.0.0" }
+assembler = { git = "git://benbridle.com/assembler", tag = "v2.0.0" }
log = { git = "git://benbridle.com/log", tag = "v1.1.2" }
switchboard = { git = "git://benbridle.com/switchboard", tag = "v1.0.0" }
paste = "1.0.15"
+indexmap = "2.7.1"
+
+[profile.release]
+lto=true
+opt-level="s"
+debug=false
+strip=true
+codegen-units=1
diff --git a/src/compiler.rs b/src/compiler.rs
index 2ad9145..10f1433 100644
--- a/src/compiler.rs
+++ b/src/compiler.rs
@@ -58,7 +58,7 @@ impl Compiler {
/// Parse all symbols from a source code string.
fn parse_symbols(source_code: &str, path: Option<&Path>) -> Vec<Symbol> {
- use syntactic::*;
+ use SyntacticTokenVariant as SynVar;
use DefinitionType::*;
use SymbolRole::*;
let mut symbols = Vec::new();
@@ -80,41 +80,41 @@ fn parse_symbols(source_code: &str, path: Option<&Path>) -> Vec<Symbol> {
}
}
- for token in SyntacticParser::from_source_code(&source_code, path) {
+ let syntactic_tokens = SyntacticParser::new(&source_code, path).parse();
+ for token in syntactic_tokens {
match token.variant {
- TokenVariant::MacroDefinition(name) => {
+ SynVar::MacroDefinition(name) => {
push!(name.clone(), token.source, Definition(MustPrecedeReference));
macro_name = Some(name);
parse_arg_list = true;
}
- TokenVariant::MacroDefinitionTerminator => {
+ SynVar::MacroDefinitionTerminator => {
macro_name = None;
}
- TokenVariant::LabelDefinition(name) => {
+ SynVar::LabelDefinition(name) => {
push!(name.clone(), token.source, Definition(CanFollowReference));
}
- TokenVariant::Symbol(name) => if parse_arg_list && after_separator {
+ SynVar::Symbol(name) => if parse_arg_list && after_separator {
push!(name, token.source, Definition(MustPrecedeReference));
} else {
parse_arg_list = false;
push!(name, token.source, Reference);
}
- TokenVariant::Separator => {
+ SynVar::Separator => {
after_separator = true;
continue;
}
- TokenVariant::BlockOpen | TokenVariant::BlockClose => {
+ SynVar::BlockOpen | SynVar::BlockClose => {
continue;
}
- TokenVariant::PackedBinaryLiteral(pbl) => {
+ SynVar::PackedBinaryLiteral(pbl) => {
for field in pbl.fields {
push!(field.name.to_string(), field.source, Reference)
}
}
- TokenVariant::ConstantExpression(expr) => {
- use ConstantExpressionTokenVariant as TokenVar;
+ SynVar::Expression(expr) => {
for token in expr.tokens {
- if let TokenVar::SymbolReference(name) = token.variant {
+ if let ExpressionTokenVariant::Invocation(name) = token.variant {
push!(name, token.source, Reference);
}
}
diff --git a/src/environment.rs b/src/environment.rs
deleted file mode 100644
index 2bb3f5b..0000000
--- a/src/environment.rs
+++ /dev/null
@@ -1,56 +0,0 @@
-use crate::*;
-use semantic::*;
-
-use std::collections::HashMap;
-
-
-pub struct Environment {
- pub scopes: Vec<Scope>,
-}
-
-impl Environment {
- pub fn get_integer(&self, name: &str) -> Result<usize, ()> {
- for scope in self.scopes.iter().rev() {
- if let Ok(value) = scope.get_integer(name, &self) {
- return Ok(value);
- }
- }
- return Err(());
- }
-
- pub fn get_block(&self, name: &str) -> Result<usize, ()> {
- for scope in self.scopes.iter().rev() {
- if let Ok(value) = scope.get_block(name, &self) {
- return Ok(value);
- }
- }
- return Err(());
- }
-}
-
-pub struct Scope {
- pub definitions: HashMap<String, Definition>,
-}
-
-impl Scope {
- pub fn get_integer(&self, _name: &str, _environment: &Environment) -> Result<usize, ()> {
- todo!()
- // use semantic::IntegerDefinition as IntDef;
-
- // if let Some(Definition { variant, ..}) = self.definitions.get(name) {
- // if let IntegerDefinition::Integer(integer) = definition {
- // match integer {
- // IntDef::Literal(value) => return Ok(*value),
- // IntDef::ConstantExpression(expr) => match expr.evaluate(environment) {
- // Ok(_) | Err(_) => todo!(),
- // },
- // };
- // }
- // }
- // return Err(());
- }
-
- pub fn get_block(&self, _name: &str, _environment: &Environment) -> Result<usize, ()> {
- todo!()
- }
-}
diff --git a/src/expression_evaluator.rs b/src/expression_evaluator.rs
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/expression_evaluator.rs
diff --git a/src/formats/inhx32.rs b/src/formats/inhx32.rs
new file mode 100644
index 0000000..d9c31d3
--- /dev/null
+++ b/src/formats/inhx32.rs
@@ -0,0 +1,87 @@
+use crate::*;
+
+
+pub fn format_inhx32(words: &[Word]) -> String {
+ let mut records = Vec::new();
+ records.push(extended_linear_address(0x0000));
+ for (i, chunk) in words.chunks(8).enumerate() {
+ records.push(data_record(chunk, (i * 8) as u16));
+ }
+ records.push(terminating_record());
+
+ let mut output = String::new();
+ for record in records {
+ output.push_str(&record.to_string());
+ }
+ return output;
+}
+
+struct Record {
+ bytes: Vec<u8>,
+}
+
+impl Record {
+ pub fn new() -> Self {
+ Self { bytes: Vec::new() }
+ }
+
+ pub fn byte(&mut self, byte: u8) {
+ self.bytes.push(byte);
+ }
+
+ pub fn be_double(&mut self, double: u16) {
+ let [high, low] = double.to_be_bytes();
+ self.byte(high);
+ self.byte(low);
+ }
+
+ pub fn le_double(&mut self, double: u16) {
+ let [high, low] = double.to_be_bytes();
+ self.byte(low);
+ self.byte(high);
+ }
+
+ pub fn to_string(self) -> String {
+ let mut sum: u8 = 0;
+ for byte in &self.bytes {
+ sum = sum.wrapping_add(*byte);
+ }
+ let checksum = sum.wrapping_neg();
+ let mut output = String::new();
+ for byte in &self.bytes {
+ output.push_str(&format!("{byte:0>2X}"));
+ }
+ format!(":{output}{checksum:0>2X}\n")
+ }
+}
+
+fn data_record(words: &[Word], address: u16) -> Record {
+ let mut record = Record::new();
+ record.byte((words.len() * 2) as u8);
+ record.be_double(address * 2);
+ record.byte(0x00);
+ for word in words {
+ match word.bits <= 16 {
+ true => record.le_double(word.value as u16),
+ false => panic!("Word '{word}' has more than 16 bits."),
+ };
+ }
+ return record;
+}
+
+fn extended_linear_address(address: u16) -> Record {
+ let mut record = Record::new();
+ record.byte(0x02);
+ record.be_double(0x0000);
+ record.byte(0x04);
+ record.be_double(address);
+ return record;
+}
+
+fn terminating_record() -> Record {
+ let mut record = Record::new();
+ record.byte(0x00);
+ record.be_double(0x0000);
+ record.byte(0x01);
+ return record;
+}
diff --git a/src/formats/mod.rs b/src/formats/mod.rs
new file mode 100644
index 0000000..42d198c
--- /dev/null
+++ b/src/formats/mod.rs
@@ -0,0 +1,2 @@
+mod inhx32;
+pub use inhx32::*;
diff --git a/src/main.rs b/src/main.rs
index e7e52d9..9fb404c 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,18 +1,21 @@
mod compiler;
-mod environment;
mod parsers;
+mod report;
mod tokens;
+mod formats;
pub use compiler::*;
-pub use environment::*;
pub use parsers::*;
+pub use report::*;
pub use tokens::*;
+pub use formats::*;
pub use assembler::*;
use log::{info, fatal};
use switchboard::{Switchboard, SwitchQuery};
use std::io::{Read, Write};
+use std::str::FromStr;
fn print_version() -> ! {
@@ -39,10 +42,13 @@ fn main() {
let no_project_libs = args.named("no-project-libs").as_bool();
let no_environment_libs = args.named("no-env-libs").as_bool();
+ let format = args.named("format").default("debug").as_string();
let print_tree = args.named("tree").as_bool();
let dry_run = args.named("dry-run").short('n').as_bool();
- let only_resolve = args.named("resolve").as_bool();
- let _export_symbols = args.named("symbols").as_bool();
+
+ let Ok(format) = Format::from_str(format.as_str()) else {
+ fatal!("Unknown format '{format}', expected 'debug', 'inhx32', 'raw', or 'source'. ");
+ };
// -----------------------------------------------------------------------
@@ -86,10 +92,11 @@ fn main() {
std::process::exit(1);
}
- let merged_source = compiler.get_compiled_source().unwrap_or_else(
- |error| { error.report(); std::process::exit(1); }
- );
- if only_resolve && !dry_run {
+ let merged_source = compiler.get_compiled_source().unwrap_or_else(|error| {
+ error.report();
+ std::process::exit(1);
+ });
+ if format == Format::Source && !dry_run {
write_bytes_and_exit(merged_source.as_bytes(), destination_path.as_ref());
}
@@ -97,12 +104,44 @@ fn main() {
// Parse syntactic tokens from merged source code.
let path = Some("<merged source>");
- let parser = SyntacticParser::from_source_code(&merged_source, path);
- let syntactic_tokens: Vec<_> = parser.collect();
+ let syntactic_tokens = SyntacticParser::new(&merged_source, path).parse();
report_syntactic_errors(&syntactic_tokens, &merged_source);
- let program = ProgramParser::new(syntactic_tokens).parse();
+ let program = SemanticParser::new(syntactic_tokens).parse();
report_semantic_errors(&program, &merged_source);
+
+ // program.print_definitions();
+ let assembled_tokens = program.assemble();
+ report_assembler_errors(&assembled_tokens, &merged_source);
+
+ let bytecode = BytecodeGenerator::new(&assembled_tokens).generate();
+ report_bytecode_errors(&bytecode, &merged_source);
+
+ if !dry_run {
+ match format {
+ Format::Debug => {
+ let mut output = String::new();
+ for word in &bytecode.words {
+ output.push_str(&word.to_string());
+ output.push('\n');
+ }
+ write_bytes_and_exit(output.as_bytes(), destination_path.as_ref());
+ }
+ Format::Inhx32 => {
+ let output = format_inhx32(&bytecode.words);
+ write_bytes_and_exit(output.as_bytes(), destination_path.as_ref());
+ }
+ Format::Raw => {
+ let mut output = Vec::new();
+ for word in &bytecode.words {
+ let value = word.value as u16;
+ output.extend(value.to_be_bytes());
+ }
+ write_bytes_and_exit(&output, destination_path.as_ref());
+ }
+ Format::Source => unreachable!(),
+ }
+ }
}
@@ -120,133 +159,23 @@ fn write_bytes_and_exit<P: AsRef<Path>>(bytes: &[u8], path: Option<&P>) -> ! {
std::process::exit(0);
}
-
-fn report_syntactic_errors(syntactic_tokens: &[syntactic::Token], source_code: &str) {
- use syntactic::*;
-
- for token in syntactic_tokens {
- let context = Context { source_code: &source_code, source: &token.source };
- match &token.variant {
- TokenVariant::ConstantExpression(expr) => for t in &expr.tokens {
- let context = Context { source_code: &source_code, source: &t.source };
- if let ConstantExpressionTokenVariant::Error(err) = &t.variant {
- let ConstantExpressionParseError::InvalidHexadecimalLiteral(hex) = err;
- let message = format!("Invalid hexadecimal literal {hex:?} in constant expression");
- report_source_issue(LogLevel::Error, &context, &message);
- }
- }
- TokenVariant::PackedBinaryLiteral(pbl) => for e in &pbl.errors {
- let context = Context { source_code: &source_code, source: &e.source };
- match e.variant {
- PackedBinaryLiteralParseErrorVariant::DuplicateFieldName(name) => {
- let message = format!("Duplicate field name {name:?} in packed binary literal");
- report_source_issue(LogLevel::Error, &context, &message);
- }
- PackedBinaryLiteralParseErrorVariant::InvalidCharacter(c) => {
- let message = format!("Invalid character {c:?} in packed binary literal");
- report_source_issue(LogLevel::Error, &context, &message);
- }
- }
- }
- TokenVariant::Error(err) => match err {
- ParseError::InvalidHexadecimalLiteral(hex) => {
- let message = format!("Invalid hexadecimal literal {hex:?}");
- report_source_issue(LogLevel::Error, &context, &message);
- }
- ParseError::InvalidSymbolIdentifier(name) => {
- let message = format!("Invalid identifier {name:?}");
- report_source_issue(LogLevel::Error, &context, &message);
- }
- ParseError::UnterminatedComment => {
- let message = format!("Unterminated comment");
- report_source_issue(LogLevel::Error, &context, &message);
- }
- ParseError::UnterminatedConstantExpression => {
- let message = format!("Unterminated constant expression");
- report_source_issue(LogLevel::Error, &context, &message);
- }
- }
- _ => (),
- }
- }
-}
-
-
-fn report_semantic_errors(program: &semantic::Program, source_code: &str) {
- for error in &program.errors {
- report_parse_error(error, source_code);
- }
- for definition in &program.definitions {
- report_definition_errors(&definition.variant, source_code);
- }
- for invocation in &program.invocations {
- report_invocation_errors(invocation, source_code);
- }
+#[derive(PartialEq)]
+enum Format {
+ Debug,
+ Inhx32,
+ Raw,
+ Source,
}
-fn report_parse_error(error: &semantic::ParseError, source_code: &str) {
- use semantic::*;
- let message = match &error.variant {
- ParseErrorVariant::UnterminatedMacroDefinition(name) =>
- format!("The macro definition '{name}' is missing a terminating ';' character"),
- ParseErrorVariant::UnterminatedBlockDefinition =>
- format!("Block literal is missing a terminating '}}' character"),
- ParseErrorVariant::InvalidArgumentDefinition(name) =>
- format!("The macro definition '{name}' has an invalid argument definition"),
- ParseErrorVariant::InvalidToken =>
- format!("Invalid token"),
- };
- let context = Context { source: &error.source, source_code};
- report_source_issue(LogLevel::Error, &context, &message);
-}
-
-fn report_definition_errors(definition: &semantic::DefinitionVariant, source_code: &str) {
- use semantic::*;
- match definition {
- DefinitionVariant::Integer(integer) => match &integer.variant {
- IntegerDefinitionVariant::Constant(expr) => for token in &expr.tokens {
- if let ConstantExpressionTokenVariant::Error(error) = &token.variant {
- let message = match error {
- ConstantExpressionParseError::InvalidHexadecimalLiteral(hex) =>
- format!("Invalid hexadecimal literal '{hex}' in constant expression"),
- };
- let context = Context { source: &token.source, source_code};
- report_source_issue(LogLevel::Error, &context, &message);
- }
- }
- _ => (),
+impl FromStr for Format {
+ type Err = ();
+ fn from_str(string: &str) -> Result<Self, ()> {
+ match string {
+ "debug" => Ok(Self::Debug),
+ "inhx32" => Ok(Self::Inhx32),
+ "raw" => Ok(Self::Raw),
+ "source" => Ok(Self::Source),
+ _ => Err(()),
}
- DefinitionVariant::Block(block) => {
- for error in &block.errors {
- report_parse_error(&error, source_code);
- }
- for token in &block.tokens {
- match &token.variant {
- BlockTokenVariant::Word(pbl) => for error in &pbl.errors {
- let message = match error.variant {
- PackedBinaryLiteralParseErrorVariant::DuplicateFieldName(name) =>
- format!("Duplicate field name '{name}' in packed binary literal"),
- PackedBinaryLiteralParseErrorVariant::InvalidCharacter(c) =>
- format!("Invalid character '{c}' in packed binary literal"),
- };
- let context = Context { source: &error.source, source_code };
- report_source_issue(LogLevel::Error, &context, &message);
- }
- BlockTokenVariant::Invocation(invocation) =>
- report_invocation_errors(invocation, source_code),
- BlockTokenVariant::Comment(_) => (),
- }
- }
- }
- DefinitionVariant::Reference(_) => (),
- }
-}
-
-fn report_invocation_errors(invocation: &semantic::Invocation, source_code: &str) {
- for error in &invocation.errors {
- report_parse_error(&error, source_code);
- }
- for argument in &invocation.arguments {
- report_definition_errors(argument, source_code);
}
}
diff --git a/src/parsers/assembler.rs b/src/parsers/assembler.rs
new file mode 100644
index 0000000..eb180e3
--- /dev/null
+++ b/src/parsers/assembler.rs
@@ -0,0 +1,282 @@
+use crate::*;
+use AssemblerErrorVariant as ErrVar;
+
+use indexmap::IndexMap;
+
+
+static mut ID: usize = 0;
+macro_rules! new_id {
+ () => { unsafe {
+ let id = ID;
+ ID += 1;
+ id
+ }};
+}
+
+
+impl SemanticProgram {
+ pub fn assemble(&self) -> Vec<AssembledToken> {
+ let environment = Environment {
+ macro_definitions: &self.macro_definitions,
+ label_definitions: &self.label_definitions,
+ arguments: &IndexMap::new(),
+ id: new_id!(),
+ };
+ let mut assembled_tokens = Vec::new();
+ for token in &self.body {
+ let tokens = environment.reify_semantic_token(token);
+ assembled_tokens.extend(tokens);
+ }
+ return assembled_tokens;
+ }
+}
+
+
+pub struct Environment<'a> {
+ pub macro_definitions: &'a IndexMap<String, MacroDefinition>,
+ pub label_definitions: &'a IndexMap<String, LabelDefinition>,
+ pub arguments: &'a IndexMap<String, Argument>,
+ pub id: usize,
+}
+
+impl<'a> Environment<'a> {
+ // This is only ever called for the highest level body tokens, never for invocations.
+ fn reify_semantic_token(&self, token: &SemanticToken) -> Vec<AssembledToken> {
+ let mut assembled_tokens = Vec::new();
+ match token {
+ SemanticToken::Word(pbl) => {
+ let word = self.reify_packed_binary_literal(pbl);
+ assembled_tokens.push(AssembledToken::Word(word));
+ }
+ SemanticToken::Invocation(invocation) => {
+ match self.reify_invocation(invocation) {
+ Ok(argument) => match argument {
+ Argument::Block(block) => assembled_tokens.extend(block),
+ Argument::Integer(_) => {
+ let variant = AssemblerErrorVariant::NotABlock;
+ let source = invocation.source.clone();
+ let error = AssemblerError { source, variant };
+ assembled_tokens.push(AssembledToken::Error(error))
+ }
+ }
+ Err(error) => assembled_tokens.push(AssembledToken::Error(error)),
+ }
+ }
+ SemanticToken::LabelDefinition(definition) => {
+ assembled_tokens.push(AssembledToken::LabelDefinition(definition.clone()));
+ }
+ SemanticToken::PinnedAddress(address) => {
+ assembled_tokens.push(AssembledToken::PinnedAddress(address.clone()));
+ }
+ SemanticToken::Error(_) => (),
+ }
+ return assembled_tokens;
+ }
+
+ fn reify_packed_binary_literal(&self, pbl: &PackedBinaryLiteral) -> AssembledWord {
+ let mut assembled_fields = Vec::new();
+ let mut errors = Vec::new();
+ for field in &pbl.fields {
+ let name = field.name.to_string();
+ match self.reify_integer_reference(&name, &field.source) {
+ Ok(value) => assembled_fields.push(
+ AssembledField {
+ source: field.source.clone(),
+ value,
+ bits: field.bits,
+ shift: field.shift,
+ }
+ ),
+ Err(error) => errors.push(error),
+ };
+ }
+ let source = pbl.source.clone();
+ let value = pbl.value;
+ let bits = pbl.bits;
+ AssembledWord { source, bits, fields: assembled_fields, value, errors }
+ }
+
+ fn reify_integer_reference(&self, name: &str, source: &SourceSpan) -> Result<IntegerArgument, AssemblerError> {
+ match self.reify_reference(name, source)? {
+ Argument::Integer(integer) => Ok(integer),
+ Argument::Block(_) => Err(
+ AssemblerError {
+ source: source.clone(),
+ variant: ErrVar::NotAnInteger,
+ }
+ ),
+ }
+ }
+
+ fn reify_reference(&self, name: &str, source: &SourceSpan) -> Result<Argument, AssemblerError> {
+ let source = source.clone();
+ if let Some(argument) = self.arguments.get(name) {
+ Ok(argument.clone())
+ } else if let Some(definition) = self.macro_definitions.get(name) {
+ self.reify_value(&definition.value)
+ } else if let Some(label) = self.label_definitions.get(name) {
+ let name = Tracked::from(self.tag_label_name(&label.name), &source);
+ Ok(Argument::Integer(IntegerArgument::LabelReference(name)))
+ } else {
+ let variant = ErrVar::DefinitionNotFound(name.to_string());
+ Err(AssemblerError { source, variant })
+ }
+ }
+
+ fn tag_label_name(&self, name: &str) -> String {
+ match name.contains(':') {
+ true => format!("{name}:{}", self.id),
+ false => name.to_string(),
+ }
+ }
+
+ fn reify_value(&self, value: &Value) -> Result<Argument, AssemblerError> {
+ match value {
+ Value::Integer(integer) => {
+ let value = match &integer {
+ Integer::Literal(integer) => {
+ IntegerArgument::Integer(integer.clone())
+ }
+ Integer::Expression(expr) => {
+ let expr = self.reify_constant_expression(expr)?;
+ IntegerArgument::Expression(expr)
+ }
+ Integer::LabelReference(name) => {
+ let name = Tracked::from(self.tag_label_name(name), &name.source);
+ IntegerArgument::LabelReference(name)
+ }
+ };
+ Ok(Argument::Integer(value))
+ }
+ Value::Block(block) => {
+ let mut assembled_tokens = Vec::new();
+ for token in block {
+ match &token {
+ SemanticToken::Word(pbl) => {
+ let word = self.reify_packed_binary_literal(pbl);
+ assembled_tokens.push(AssembledToken::Word(word));
+ }
+ SemanticToken::Invocation(invocation) => {
+ match self.reify_invocation(invocation)? {
+ Argument::Block(block) => assembled_tokens.extend(block),
+ Argument::Integer(_) => {
+ let source = invocation.source.clone();
+ let variant = AssemblerErrorVariant::IntegerInBlock;
+ return Err(AssemblerError { source, variant});
+ }
+ }
+ }
+ SemanticToken::LabelDefinition(definition) => {
+ let mut definition = definition.clone();
+ definition.name.push_str(&format!(":{}", self.id));
+ let token = AssembledToken::LabelDefinition(definition);
+ assembled_tokens.push(token);
+ }
+ SemanticToken::PinnedAddress(address) => {
+ let token = AssembledToken::PinnedAddress(address.to_owned());
+ assembled_tokens.push(token);
+ }
+ SemanticToken::Error(_) => (),
+ }
+ }
+ Ok(Argument::Block(assembled_tokens))
+ }
+ Value::Invocation(invocation) => {
+ self.reify_invocation(invocation)
+ }
+ }
+ }
+
+ fn reify_invocation(&self, invocation: &Invocation) -> Result<Argument, AssemblerError> {
+ macro_rules! err {
+ ($variant:expr) => { Err(AssemblerError {
+ source: invocation.source.clone(), variant: $variant
+ }) };
+ }
+ if let Some(argument) = self.arguments.get(&invocation.name) {
+ let expected = 0;
+ let received = invocation.arguments.len();
+ if received != expected {
+ return err!(ErrVar::IncorrectArgumentCount(expected, received));
+ }
+ Ok(argument.clone())
+ } else if let Some(definition) = self.macro_definitions.get(&invocation.name) {
+ // Check that the correct number of arguments were provided.
+ let received = invocation.arguments.len();
+ let expected = definition.arguments.len();
+ if received != expected {
+ return err!(ErrVar::IncorrectArgumentCount(expected, received));
+ }
+ let mut arguments = IndexMap::new();
+ for (i, argument) in invocation.arguments.iter().enumerate() {
+ // Check that the correct types of arguments were provided.
+ let arg_invocation = self.reify_value(&argument.value)?;
+ let arg_invocation_type = match &arg_invocation {
+ Argument::Integer(_) => ArgumentVariant::Integer,
+ Argument::Block(_) => ArgumentVariant::Block,
+ };
+ let arg_definition_type = definition.arguments[i].variant;
+ if arg_invocation_type != arg_definition_type {
+ let variant = ErrVar::IncorrectArgumentType(
+ arg_definition_type, arg_invocation_type
+ );
+ return Err(AssemblerError { source: argument.source.clone(), variant });
+ }
+ let name = definition.arguments[i].name.clone();
+ arguments.insert(name, arg_invocation);
+ }
+ let environment = Environment {
+ macro_definitions: &self.macro_definitions,
+ label_definitions: &self.label_definitions,
+ arguments: &arguments,
+ id: new_id!(),
+ };
+ environment.reify_value(&definition.value)
+ } else if let Some(label) = self.label_definitions.get(&invocation.name) {
+ let expected = 0;
+ let received = invocation.arguments.len();
+ if received != expected {
+ return err!(ErrVar::IncorrectArgumentCount(expected, received));
+ }
+ let name = Tracked::from(self.tag_label_name(&label.name), &label.source);
+ Ok(Argument::Integer(IntegerArgument::LabelReference(name)))
+ } else {
+ err!(ErrVar::DefinitionNotFound(invocation.name.to_string()))
+ }
+ }
+
+ fn reify_constant_expression(&self, expr: &Expression) -> Result<AssembledExpression, AssemblerError> {
+ use ExpressionTokenVariant as ExprVar;
+
+ let mut assembled_tokens = Vec::new();
+ for token in &expr.tokens {
+ let assembled_token = match &token.variant {
+ ExprVar::Literal(value) => {
+ let source = token.source.clone();
+ let integer = TrackedInteger { source, value: *value };
+ AssembledExpressionToken::Integer(integer)
+ }
+ ExprVar::Operator(operator) => {
+ AssembledExpressionToken::Operator(*operator)
+ }
+ ExprVar::Invocation(name) => {
+ match self.reify_integer_reference(&name, &token.source)? {
+ IntegerArgument::LabelReference(name) => {
+ AssembledExpressionToken::LabelReference(name)
+ }
+ IntegerArgument::Integer(integer) => {
+ AssembledExpressionToken::Integer(integer)
+ }
+ IntegerArgument::Expression(expr) => {
+ AssembledExpressionToken::Expression(Box::new(expr))
+ },
+ }
+ }
+ ExprVar::Error(_) => continue,
+ };
+ assembled_tokens.push(assembled_token);
+ }
+ Ok(AssembledExpression { source: expr.source.clone(), tokens: assembled_tokens })
+ }
+}
+
diff --git a/src/parsers/bytecode.rs b/src/parsers/bytecode.rs
new file mode 100644
index 0000000..ec19d9f
--- /dev/null
+++ b/src/parsers/bytecode.rs
@@ -0,0 +1,161 @@
+use crate::*;
+
+use std::collections::HashMap;
+
+
+pub struct BytecodeGenerator<'a> {
+ tokens: &'a [AssembledToken],
+ addresses: HashMap<String, Tracked<usize>>,
+ words: Vec<Word>,
+ errors: Vec<BytecodeError>,
+}
+
+impl<'a> BytecodeGenerator<'a> {
+ pub fn new(tokens: &'a [AssembledToken]) -> Self {
+ Self {
+ tokens,
+ addresses: HashMap::new(),
+ words: Vec::new(),
+ errors: Vec::new(),
+ }
+ }
+
+ pub fn generate(mut self) -> Bytecode {
+ self.calculate_addresses();
+ for token in self.tokens {
+ match token {
+ AssembledToken::Word(assembled_word) => {
+ let mut value = assembled_word.value;
+ for field in &assembled_word.fields {
+ let (field_value, source) = match &field.value {
+ IntegerArgument::Expression(expr) =>
+ (self.resolve_expression(expr), expr.source.clone()),
+ IntegerArgument::LabelReference(name) =>
+ (self.resolve_label_reference(name), name.source.clone()),
+ IntegerArgument::Integer(integer) =>
+ (integer.value, integer.source.clone()),
+ };
+ let bitcount = match field_value {
+ 0 => 0,
+ _ => (field_value.ilog2() + 1) as usize,
+ };
+ if field.bits < bitcount {
+ let variant = BytecodeErrorVariant::ValueTooLarge(field.bits, bitcount);
+ self.errors.push(BytecodeError { source, variant });
+ } else {
+ value |= (field_value << field.shift) as usize;
+ }
+ }
+ self.words.push(Word { bits: assembled_word.bits, value });
+ }
+ AssembledToken::PinnedAddress(pinned) => {
+ if self.words.len() > pinned.address {
+ let variant = BytecodeErrorVariant::PinnedAddressBacktrack(
+ pinned.address, self.words.len());
+ let source = pinned.source.clone();
+ self.errors.push(BytecodeError { source, variant });
+ } else {
+ self.words.resize(pinned.address, Word { bits: 0, value: 0});
+ }
+ }
+ AssembledToken::LabelDefinition(_) => (),
+ AssembledToken::Error(_) => (),
+ }
+ }
+
+ return Bytecode {
+ words: self.words,
+ errors: self.errors,
+ }
+ }
+
+ fn calculate_addresses(&mut self) {
+ let mut i = 0;
+ for token in self.tokens {
+ match token {
+ AssembledToken::LabelDefinition(definition) => {
+ let address = Tracked::from(i, &definition.source);
+ if let Some(_) = self.addresses.insert(definition.name.clone(), address) {
+ let name = definition.name.clone();
+ let variant = BytecodeErrorVariant::DuplicateLabelDefinition(name);
+ let source = definition.source.clone();
+ self.errors.push(BytecodeError { source, variant });
+ }
+ }
+ AssembledToken::Word(_) => {
+ i += 1;
+ }
+ AssembledToken::PinnedAddress(pinned) => {
+ i = pinned.address;
+ }
+ AssembledToken::Error(_) => (),
+ }
+ }
+ }
+
+ fn resolve_expression(&mut self, expr: &AssembledExpression) -> isize {
+ let mut stack = Vec::new();
+ macro_rules! push {
+ ($value:expr) => { stack.push($value) };
+ }
+ macro_rules! pop {
+ ($name:ident) => { let $name = match stack.pop() {
+ Some(value) => value,
+ None => {
+ let variant = BytecodeErrorVariant::StackUnderflow;
+ self.errors.push(BytecodeError { source: expr.source.clone(), variant });
+ return 0;
+ },
+ }; };
+ }
+ macro_rules! truth {
+ ($bool:expr) => { match $bool { true => 1, false => 0 } };
+ }
+
+ for token in &expr.tokens {
+ match &token {
+ AssembledExpressionToken::Integer(value) => {
+ push!(value.value)
+ }
+ AssembledExpressionToken::LabelReference(name) => {
+ push!(self.resolve_label_reference(name))
+ }
+ AssembledExpressionToken::Expression(expr) => {
+ push!(self.resolve_expression(expr))
+ }
+ AssembledExpressionToken::Operator(operator) => match operator {
+ Operator::Equal => { pop!(b); pop!(a); push!(truth!(a==b)) },
+ Operator::NotEqual => { pop!(b); pop!(a); push!(truth!(a!=b)) },
+ Operator::LessThan => { pop!(b); pop!(a); push!(truth!(a < b)) },
+ Operator::GreaterThan => { pop!(b); pop!(a); push!(truth!(a > b)) },
+ Operator::Add => { pop!(b); pop!(a); push!(a + b) },
+ Operator::Subtract => { pop!(b); pop!(a); push!(a - b) },
+ Operator::LeftShift => { pop!(b); pop!(a); push!(a << b) },
+ Operator::RightShift => { pop!(b); pop!(a); push!(a >> b) },
+ Operator::And => { pop!(b); pop!(a); push!(a & b) },
+ Operator::Or => { pop!(b); pop!(a); push!(a | b) },
+ Operator::Xor => { pop!(b); pop!(a); push!(a ^ b) },
+ Operator::Not => { pop!(a); push!(!a) },
+ }
+ }
+ }
+
+ let variant = match stack.len() {
+ 0 => BytecodeErrorVariant::NoReturnValue,
+ 1 => return stack[0],
+ _ => BytecodeErrorVariant::MultipleReturnValues,
+ };
+ self.errors.push(BytecodeError { source: expr.source.clone(), variant});
+ 0
+ }
+
+ fn resolve_label_reference(&mut self, name: &Tracked<String>) -> isize {
+ if let Some(address) = self.addresses.get(&name.value) {
+ address.value as isize
+ } else {
+ let variant = BytecodeErrorVariant::DefinitionNotFound(name.value.clone());
+ self.errors.push(BytecodeError { source: name.source.clone(), variant });
+ 0
+ }
+ }
+}
diff --git a/src/parsers/constant_expression.rs b/src/parsers/expression.rs
index 78dc697..f902858 100644
--- a/src/parsers/constant_expression.rs
+++ b/src/parsers/expression.rs
@@ -1,17 +1,15 @@
use crate::*;
-pub fn parse_constant_expression(string: &str, parent: &Tokeniser) -> ConstantExpression {
- use ConstantExpressionTokenVariant as TokenVar;
- use ConstantExpressionParseError as ParseError;
+pub fn parse_constant_expression(mut t: Tokeniser, source: SourceSpan) -> Expression {
+ use ExpressionTokenVariant as TokenVar;
+ use ExpressionParseError as ParseError;
let mut tokens = Vec::new();
- let mut t = Tokeniser::new_child(string, parent);
- t.position.to_next_char(); // skip opening delimiter
loop {
- t.drop_whitespace();
- t.mark_start_position();
+ t.eat_whitespace();
+ t.mark_start();
let token = t.eat_token();
if token.is_empty() {
break;
@@ -19,7 +17,7 @@ pub fn parse_constant_expression(string: &str, parent: &Tokeniser) -> ConstantEx
let variant = match token.as_str() {
"=" => TokenVar::Operator(Operator::Equal),
- "!" => TokenVar::Operator(Operator::NotEqual),
+ "!=" => TokenVar::Operator(Operator::NotEqual),
"<" => TokenVar::Operator(Operator::LessThan),
">" => TokenVar::Operator(Operator::GreaterThan),
"+" => TokenVar::Operator(Operator::Add),
@@ -32,21 +30,21 @@ pub fn parse_constant_expression(string: &str, parent: &Tokeniser) -> ConstantEx
"~" => TokenVar::Operator(Operator::Not),
_ => if let Some(stripped) = token.strip_prefix("0x") {
match usize::from_str_radix(stripped, 16) {
- Ok(value) => TokenVar::IntegerLiteral(value),
+ Ok(value) => TokenVar::Literal(value as isize),
Err(_) => TokenVar::Error(
ParseError::InvalidHexadecimalLiteral(stripped.to_string())),
}
} else {
match usize::from_str_radix(&token, 10) {
- Ok(value) => TokenVar::IntegerLiteral(value),
- Err(_) => TokenVar::SymbolReference(token.to_string()),
+ Ok(value) => TokenVar::Literal(value as isize),
+ Err(_) => TokenVar::Invocation(token.to_string()),
}
}
};
- let source = t.mark_end_position();
- tokens.push(ConstantExpressionToken { source, variant });
+ let source = t.get_source();
+ tokens.push(ExpressionToken { source, variant });
}
- return ConstantExpression { tokens };
+ return Expression { source, tokens };
}
diff --git a/src/parsers/mod.rs b/src/parsers/mod.rs
index 91765a9..da2c23a 100644
--- a/src/parsers/mod.rs
+++ b/src/parsers/mod.rs
@@ -1,11 +1,15 @@
-mod constant_expression;
-pub use constant_expression::*;
-
+mod expression;
mod packed_binary_literal;
+
+pub use expression::*;
pub use packed_binary_literal::*;
mod syntactic;
-pub use syntactic::*;
-
mod semantic;
+mod assembler;
+mod bytecode;
+
+pub use syntactic::*;
pub use semantic::*;
+pub use assembler::*;
+pub use bytecode::*;
diff --git a/src/parsers/packed_binary_literal.rs b/src/parsers/packed_binary_literal.rs
index 9704fc4..18f8da7 100644
--- a/src/parsers/packed_binary_literal.rs
+++ b/src/parsers/packed_binary_literal.rs
@@ -1,53 +1,54 @@
use crate::*;
-pub fn parse_packed_binary_literal(string: &str, parent: &Tokeniser) -> PackedBinaryLiteral {
+/// t is a Tokeniser over the characters of the PBL, excluding the leading hash.
+pub fn parse_packed_binary_literal(mut t: Tokeniser, source: SourceSpan) -> PackedBinaryLiteral {
use PackedBinaryLiteralParseError as ParseError;
use PackedBinaryLiteralParseErrorVariant as ParseErrorVar;
let mut value = 0;
let mut bits = 0;
+ let mut field_bits = 0;
let mut name = '\0';
let mut fields: Vec<BitField> = Vec::new();
let mut errors: Vec<ParseError> = Vec::new();
macro_rules! push_field {
- ($source:expr) => {
+ () => {
if fields.iter().any(|f| f.name == name) {
let variant = ParseErrorVar::DuplicateFieldName(name);
- errors.push(ParseError { source: $source, variant });
+ errors.push(ParseError { source: t.get_source(), variant });
} else {
- fields.push(BitField { name, source: $source, bits, shift: 0 });
+ fields.push(BitField { name, source: t.get_source(), bits: field_bits, shift: 0 });
}
};
}
- let mut t = Tokeniser::new_child(string, parent);
- t.position.to_next_char(); // skip opening hash character
-
while let Some(c) = t.eat_char() {
// Ignore underscores.
if c == '_' {
- t.prev_position = t.prev_prev_position;
+ t.mark.undo();
continue;
}
// Add a bit to the value;
value <<= 1;
+ bits += 1;
for field in &mut fields {
field.shift += 1;
}
// Extend the current field.
if c == name {
- bits += 1;
+ field_bits += 1;
continue;
}
// Commit the current field.
- if bits > 0 {
- push_field!(t.mark_prev_end_position());
- bits = 0;
+ if field_bits > 0 {
+ t.mark_end_prev();
+ push_field!();
+ field_bits = 0;
name = '\0';
}
@@ -60,21 +61,25 @@ pub fn parse_packed_binary_literal(string: &str, parent: &Tokeniser) -> PackedBi
continue;
}
- t.mark_prev_start_position();
+ t.mark_start_prev();
if c.is_alphabetic() {
name = c;
- bits = 1;
+ field_bits = 1;
continue;
} else {
- let source = t.mark_end_position();
- errors.push(ParseError { source, variant: ParseErrorVar::InvalidCharacter(c) });
+ let source = t.get_source();
+ let variant = ParseErrorVar::InvalidCharacter(c);
+ errors.push(ParseError { source, variant });
}
}
// Commit the final field.
- if bits > 0 {
- push_field!(t.mark_end_position());
+ for field in &mut fields {
+ field.shift += 1;
+ }
+ if field_bits > 0 {
+ push_field!();
}
- PackedBinaryLiteral { value, fields, errors }
+ PackedBinaryLiteral { source, bits, value, fields, errors }
}
diff --git a/src/parsers/semantic.rs b/src/parsers/semantic.rs
index 7ef4a4a..a58fb5f 100644
--- a/src/parsers/semantic.rs
+++ b/src/parsers/semantic.rs
@@ -1,136 +1,106 @@
use crate::*;
-
-use syntactic as syn;
-use syn::TokenVariant as SynVar;
-use semantic::*;
+use SyntacticTokenVariant as SynVar;
use std::collections::VecDeque;
+use indexmap::IndexMap;
+
macro_rules! fn_is_syn_variant {
($name:ident, $variant:ty) => { paste::paste! {
- fn [< is_ $name >](token: &syn::Token) -> bool {
+ fn [< is_ $name >](token: &SyntacticToken) -> bool {
match token.variant { $variant => true, _ => false, }
} } }; }
-fn_is_syn_variant!(block_open, syn::TokenVariant::BlockOpen);
-fn_is_syn_variant!(block_close, syn::TokenVariant::BlockClose);
-fn_is_syn_variant!(separator, syn::TokenVariant::Separator);
-fn_is_syn_variant!(terminator, syn::TokenVariant::MacroDefinitionTerminator);
-
-
-pub struct Tokens {
- tokens: VecDeque<syn::Token>,
-}
-
-impl Tokens {
- pub fn new<T: Into<VecDeque<syn::Token>>>(tokens: T) -> Self {
- Self { tokens: tokens.into() }
- }
-
- pub fn pop(&mut self) -> Option<syn::Token> {
- self.tokens.pop_front()
- }
+fn_is_syn_variant!(block_open, SyntacticTokenVariant::BlockOpen);
+fn_is_syn_variant!(block_close, SyntacticTokenVariant::BlockClose);
+fn_is_syn_variant!(separator, SyntacticTokenVariant::Separator);
+fn_is_syn_variant!(terminator, SyntacticTokenVariant::MacroDefinitionTerminator);
- pub fn pop_if(&mut self, predicate: fn(&syn::Token) -> bool) -> Option<syn::Token> {
- match predicate(self.tokens.front()?) {
- true => self.tokens.pop_front(),
- false => None,
- }
- }
- pub fn unpop(&mut self, token: syn::Token) {
- self.tokens.push_front(token);
- }
-
- /// Pull tokens until the predicate returns true, otherwise return Err.
- pub fn pull_until(&mut self, mut predicate: impl FnMut(&syn::Token) -> bool) -> Result<Self, ()> {
- let mut output = VecDeque::new();
- while let Some(token) = self.tokens.pop_front() {
- match predicate(&token) {
- true => return Ok(Self::new(output)),
- false => output.push_back(token),
- };
- }
- return Err(());
- }
-
- pub fn take(&mut self) -> Self {
- Self { tokens: std::mem::take(&mut self.tokens) }
- }
-
- pub fn len(&self) -> usize {
- self.tokens.len()
- }
-}
-
-
-pub struct ProgramParser {
+pub struct SemanticParser {
tokens: Tokens,
- definitions: Vec<Definition>,
- invocations: Vec<Invocation>,
- errors: Vec<ParseError>,
+ macro_definitions: IndexMap<String, MacroDefinition>,
+ label_definitions: IndexMap<String, LabelDefinition>,
+ body: Vec<SemanticToken>,
}
-impl ProgramParser {
- pub fn new(syntactic_tokens: Vec<syn::Token>) -> Self {
+impl SemanticParser {
+ pub fn new(syntactic_tokens: Vec<SyntacticToken>) -> Self {
+ // Gather all labels ahead of time.
+ let mut label_definitions = IndexMap::new();
+ for token in &syntactic_tokens {
+ if let SyntacticTokenVariant::LabelDefinition(name) = &token.variant {
+ let definition = LabelDefinition {
+ source: token.source.clone(),
+ name: name.clone(),
+ };
+ let None = label_definitions.insert(name.to_string(), definition) else {
+ unreachable!("Duplicate definition for label {name:?}");
+ };
+ }
+ }
Self {
tokens: Tokens::new(syntactic_tokens),
- definitions: Vec::new(),
- invocations: Vec::new(),
- errors: Vec::new(),
+ macro_definitions: IndexMap::new(),
+ label_definitions,
+ body: Vec::new(),
}
}
- pub fn parse(mut self) -> Program {
+ pub fn parse(mut self) -> SemanticProgram {
while let Some(syn) = self.tokens.pop() {
match syn.variant {
SynVar::MacroDefinition(name) => {
- // Collect all tokens up to the next definition terminator.
let Ok(definition_tokens) = self.tokens.pull_until(is_terminator) else {
- let variant = ParseErrorVariant::UnterminatedMacroDefinition(name);
- self.errors.push(ParseError { source: syn.source, variant});
+ let variant = SemanticParseErrorVariant::UnterminatedMacroDefinition(name);
+ let error = SemanticParseError { source: syn.source, variant };
+ self.body.push(SemanticToken::Error(error));
break;
};
- // Parse macro definition arguments.
- match DefinitionParser::new(name, syn.source, definition_tokens).parse() {
- Ok(definition) => self.definitions.push(definition),
- Err(errors) => self.errors.extend(errors),
+ let definition = MacroDefinitionParser::new(syn.source, definition_tokens).parse();
+ let None = self.macro_definitions.insert(name.clone(), definition) else {
+ unreachable!("Duplicate definition for macro {name}");
};
}
- SynVar::Comment(_) => (),
+ SynVar::LabelDefinition(name) => {
+ let label_definition = LabelDefinition { source: syn.source, name };
+ self.body.push(SemanticToken::LabelDefinition(label_definition));
+ }
+ SynVar::PinnedAddress(address) => {
+ let pinned_address = PinnedAddress { source: syn.source, address };
+ self.body.push(SemanticToken::PinnedAddress(pinned_address));
+ }
SynVar::Symbol(name) => {
- let parser = InvocationParser::new(name, &mut self.tokens);
- self.invocations.push(parser.parse());
+ let invocation = InvocationParser::new(name, syn.source, &mut self.tokens).parse();
+ self.body.push(SemanticToken::Invocation(invocation));
}
_ => {
- let variant = ParseErrorVariant::InvalidToken;
- self.errors.push(ParseError { source: syn.source, variant});
- break;
+ let variant = SemanticParseErrorVariant::InvalidToken;
+ let error = SemanticParseError { source: syn.source, variant };
+ self.body.push(SemanticToken::Error(error));
}
}
}
- Program {
- definitions: self.definitions,
- invocations: self.invocations,
- errors: self.errors,
+ SemanticProgram {
+ macro_definitions: self.macro_definitions,
+ label_definitions: self.label_definitions,
+ body: self.body,
}
}
}
-pub struct DefinitionParser {
- name: String,
+pub struct MacroDefinitionParser {
source: SourceSpan,
tokens: Tokens,
arguments: Vec<ArgumentDefinition>,
- errors: Vec<ParseError>,
+ errors: Vec<SemanticParseError>,
}
-impl DefinitionParser {
- pub fn new(name: String, source: SourceSpan, tokens: Tokens) -> Self {
+impl MacroDefinitionParser {
+ pub fn new(source: SourceSpan, tokens: Tokens) -> Self {
Self {
- name,
tokens,
source,
arguments: Vec::new(),
@@ -138,20 +108,15 @@ impl DefinitionParser {
}
}
- pub fn parse(mut self) -> Result<Definition, Vec<ParseError>> {
+ pub fn parse(mut self) -> MacroDefinition {
while let Some(definition) = self.parse_argument_definition() {
self.arguments.push(definition)
}
- if self.errors.is_empty() {
- let variant = self.parse_body();
- Ok(Definition {
- name: self.name,
- source: self.source,
- arguments: self.arguments,
- variant,
- })
- } else {
- Err(self.errors)
+ MacroDefinition {
+ value: self.parse_body(),
+ source: self.source,
+ arguments: self.arguments,
+ errors: self.errors,
}
}
@@ -172,47 +137,45 @@ impl DefinitionParser {
let token = token?;
let source = token.source;
if let SynVar::Symbol(name) = token.variant {
- let variant = ArgumentDefinitionVariant::Integer;
+ let variant = match is_block {
+ true => ArgumentVariant::Block,
+ false => ArgumentVariant::Integer,
+ };
Some(ArgumentDefinition { name, source, variant })
} else {
- let name = self.name.clone();
- let variant = ParseErrorVariant::InvalidArgumentDefinition(name);
- self.errors.push(ParseError { source, variant});
+ let variant = SemanticParseErrorVariant::InvalidToken;
+ self.errors.push(SemanticParseError { source, variant});
None
}
}
- fn parse_body(&mut self) -> DefinitionVariant {
- // Attempt to parse an IntegerDefinition.
+ fn parse_body(&mut self) -> Value {
+ // Attempt to parse an Integer.
if self.tokens.len() == 1 {
let token = self.tokens.pop().unwrap();
match token.variant {
- SynVar::DecimalLiteral(value) | SynVar::HexadecimalLiteral(value) => {
- return DefinitionVariant::Integer(IntegerDefinition {
- source: token.source,
- variant: IntegerDefinitionVariant::Literal(value),
- });
+ SynVar::IntegerLiteral(value) => {
+ let integer = TrackedInteger { source: token.source, value };
+ return Value::Integer(Integer::Literal(integer));
}
- SynVar::ConstantExpression(expr) => {
- return DefinitionVariant::Integer(IntegerDefinition {
- source: token.source,
- variant: IntegerDefinitionVariant::Constant(expr),
- });
- }
- SynVar::Symbol(name) => {
- return DefinitionVariant::Reference(ReferenceDefinition {
- source: token.source,
- name,
- });
+ SynVar::Expression(expr) => {
+ return Value::Integer(Integer::Expression(expr));
}
_ => (),
}
self.tokens.unpop(token);
}
-
- // Parse the remaining tokens as a BlockDefinition.
- let block = BlockParser::new(self.tokens.take()).parse();
- return DefinitionVariant::Block(block);
+ // Parse a Block.
+ let mut block = BlockParser::new(self.tokens.take()).parse();
+ // If the block contains a single invocation, unwrap it.
+ if block.len() == 1 {
+ match block.pop() {
+ Some(SemanticToken::Invocation(invocation)) => return Value::Invocation(invocation),
+ Some(other) => block.push(other),
+ None => (),
+ };
+ }
+ return Value::Block(block);
}
}
@@ -220,52 +183,52 @@ impl DefinitionParser {
/// Parse an entire block, excluding delimiters.
pub struct BlockParser {
tokens: Tokens,
- block_tokens: Vec<BlockToken>,
- errors: Vec<ParseError>,
+ semantic_tokens: Vec<SemanticToken>,
}
impl BlockParser {
pub fn new(tokens: Tokens) -> Self {
- Self { tokens, block_tokens: Vec::new(), errors: Vec::new() }
+ Self { tokens, semantic_tokens: Vec::new() }
}
- pub fn parse(mut self) -> BlockDefinition {
+ pub fn parse(mut self) -> Vec<SemanticToken> {
while let Some(token) = self.tokens.pop() {
let source = token.source;
match token.variant {
SynVar::Symbol(name) => {
- let parser = InvocationParser::new(name, &mut self.tokens);
- let invocation = parser.parse();
- let variant = BlockTokenVariant::Invocation(invocation);
- let block_token = BlockToken { source, variant };
- self.block_tokens.push(block_token);
+ let invocation = InvocationParser::new(name, source, &mut self.tokens).parse();
+ self.semantic_tokens.push(SemanticToken::Invocation(invocation));
}
SynVar::PackedBinaryLiteral(pbl) => {
- let variant = BlockTokenVariant::Word(pbl);
- let block_token = BlockToken { source, variant };
- self.block_tokens.push(block_token);
+ self.semantic_tokens.push(SemanticToken::Word(pbl));
+ }
+ SynVar::LabelDefinition(name) => {
+ let label_definition = LabelDefinition { source, name };
+ self.semantic_tokens.push(SemanticToken::LabelDefinition(label_definition));
}
_ => {
- let variant = ParseErrorVariant::InvalidToken;
- self.errors.push(ParseError { source, variant })
+ let variant = SemanticParseErrorVariant::InvalidToken;
+ let error = SemanticParseError { source, variant };
+ self.semantic_tokens.push(SemanticToken::Error(error));
}
}
}
- BlockDefinition { tokens: self.block_tokens, errors: self.errors }
+ return self.semantic_tokens;
}
}
struct InvocationParser<'a> {
name: String,
+ source: SourceSpan,
tokens: &'a mut Tokens,
- arguments: Vec<DefinitionVariant>,
- errors: Vec<ParseError>,
+ arguments: Vec<ArgumentInvocation>,
+ errors: Vec<SemanticParseError>,
}
impl<'a> InvocationParser<'a> {
- pub fn new(name: String, tokens: &'a mut Tokens) -> Self {
- Self { name, tokens, arguments: Vec::new(), errors: Vec::new() }
+ pub fn new(name: String, source: SourceSpan, tokens: &'a mut Tokens) -> Self {
+ Self { name, source, tokens, arguments: Vec::new(), errors: Vec::new() }
}
pub fn parse(mut self) -> Invocation {
@@ -274,33 +237,34 @@ impl<'a> InvocationParser<'a> {
}
Invocation {
name: self.name,
+ source: self.source,
arguments: self.arguments,
errors: self.errors,
}
}
- fn parse_invocation_argument(&mut self) -> Option<DefinitionVariant> {
+ fn parse_invocation_argument(&mut self) -> Option<ArgumentInvocation> {
// Only continue if the first token is a separator.
self.tokens.pop_if(is_separator)?;
if let Some(block_open) = self.tokens.pop_if(is_block_open) {
let source = block_open.source;
let mut depth = 1;
- let is_matching_block_close = |token: &syntactic::Token| {
+ let is_matching_block_close = |token: &SyntacticToken| {
match token.variant {
- syntactic::TokenVariant::BlockOpen => {
+ SyntacticTokenVariant::BlockOpen => {
depth += 1; false }
- syntactic::TokenVariant::BlockClose => {
+ SyntacticTokenVariant::BlockClose => {
depth -= 1; depth == 0 }
_ => false,
}
};
if let Ok(block_tokens) = self.tokens.pull_until(is_matching_block_close) {
let block = BlockParser::new(block_tokens).parse();
- Some(DefinitionVariant::Block(block))
+ Some(ArgumentInvocation { source, value: Value::Block(block) })
} else {
- let variant = ParseErrorVariant::UnterminatedBlockDefinition;
- self.errors.push(ParseError { source, variant });
+ let variant = SemanticParseErrorVariant::UnterminatedBlock;
+ self.errors.push(SemanticParseError { source, variant });
None
}
} else {
@@ -308,25 +272,74 @@ impl<'a> InvocationParser<'a> {
let source = token.source;
match token.variant {
SynVar::Symbol(name) => {
- let reference = ReferenceDefinition { source, name };
- Some(DefinitionVariant::Reference(reference))
+ let arguments = Vec::new();
+ let errors = Vec::new();
+ let invocation = Invocation { source: source.clone(), name, arguments, errors };
+ let value = Value::Invocation(invocation);
+ Some(ArgumentInvocation { source, value })
}
- SynVar::DecimalLiteral(value) | SynVar::HexadecimalLiteral(value) => {
- let variant = IntegerDefinitionVariant::Literal(value);
- let integer = IntegerDefinition { source, variant };
- Some(DefinitionVariant::Integer(integer))
+ SynVar::IntegerLiteral(value) => {
+ let integer = TrackedInteger { source: source.clone(), value };
+ let value = Value::Integer(Integer::Literal(integer));
+ Some(ArgumentInvocation { source, value })
}
- SynVar::ConstantExpression(expr) => {
- let variant = IntegerDefinitionVariant::Constant(expr);
- let integer = IntegerDefinition { source, variant };
- Some(DefinitionVariant::Integer(integer))
+ SynVar::Expression(expr) => {
+ let value = Value::Integer(Integer::Expression(expr));
+ Some(ArgumentInvocation { source, value })
}
_ => {
- let variant = ParseErrorVariant::InvalidToken;
- self.errors.push(ParseError { source, variant });
+ let variant = SemanticParseErrorVariant::InvalidToken;
+ self.errors.push(SemanticParseError { source, variant });
None
}
}
}
}
}
+
+
+pub struct Tokens {
+ tokens: VecDeque<SyntacticToken>,
+}
+
+impl Tokens {
+ pub fn new<T: Into<VecDeque<SyntacticToken>>>(tokens: T) -> Self {
+ Self { tokens: tokens.into() }
+ }
+
+ pub fn pop(&mut self) -> Option<SyntacticToken> {
+ self.tokens.pop_front()
+ }
+
+ pub fn pop_if(&mut self, predicate: fn(&SyntacticToken) -> bool) -> Option<SyntacticToken> {
+ match predicate(self.tokens.front()?) {
+ true => self.tokens.pop_front(),
+ false => None,
+ }
+ }
+
+ pub fn unpop(&mut self, token: SyntacticToken) {
+ self.tokens.push_front(token);
+ }
+
+ /// Pull tokens until the predicate returns true, otherwise return Err.
+ pub fn pull_until(&mut self, mut predicate: impl FnMut(&SyntacticToken) -> bool) -> Result<Self, ()> {
+ let mut output = VecDeque::new();
+ while let Some(token) = self.tokens.pop_front() {
+ match predicate(&token) {
+ true => return Ok(Self::new(output)),
+ false => output.push_back(token),
+ };
+ }
+ return Err(());
+ }
+
+ pub fn take(&mut self) -> Self {
+ Self { tokens: std::mem::take(&mut self.tokens) }
+ }
+
+ pub fn len(&self) -> usize {
+ self.tokens.len()
+ }
+}
+
diff --git a/src/parsers/syntactic.rs b/src/parsers/syntactic.rs
index 909dbaa..37f8e6c 100644
--- a/src/parsers/syntactic.rs
+++ b/src/parsers/syntactic.rs
@@ -1,106 +1,147 @@
use crate::*;
-use syntactic::*;
pub struct SyntacticParser {
tokeniser: Tokeniser,
- /// The name of the most recently parsed label.
- label_name: String,
+ tokens: Vec<SyntacticToken>,
/// The name of the macro being parsed.
macro_name: Option<String>,
+ /// The name of the most recent label.
+ label_name: String,
}
impl SyntacticParser {
- pub fn from_source_code<P: Into<PathBuf>>(source_code: &str, path: Option<P>) -> Self {
+ pub fn new<P: Into<PathBuf>>(source_code: &str, path: Option<P>) -> Self {
let mut tokeniser = Tokeniser::new(source_code, path);
- tokeniser.add_delimiters(&['@','&','%',';',':','{','}','(','[','#','~']);
+ tokeniser.add_delimiters(&['@','%',';',':','{','}','(','[','#','~']);
Self {
tokeniser,
- label_name: String::new(),
+ tokens: Vec::new(),
macro_name: None,
+ label_name: String::new(),
}
}
-}
-
-impl Iterator for SyntacticParser {
- type Item = Token;
-
- /// Sequentially parse tokens from the source code.
- fn next(&mut self) -> Option<Token> {
+ pub fn parse(mut self) -> Vec<SyntacticToken> {
+ use SyntacticTokenVariant as SynVar;
+ use SyntacticParseError as SynErr;
let t = &mut self.tokeniser;
- t.drop_whitespace();
- t.mark_start_position();
- let variant = match t.eat_char()? {
- '@' => {
- self.label_name = t.eat_token();
- TokenVariant::LabelDefinition(self.label_name.clone())
- }
- '&' => {
- let token = t.eat_token();
- TokenVariant::LabelDefinition(format!("{}/{token}", self.label_name))
- }
- '%' => {
- let macro_name = t.eat_token();
- self.macro_name = Some(macro_name.clone());
- TokenVariant::MacroDefinition(macro_name)
- }
- ';' => {
- self.macro_name = None;
- TokenVariant::MacroDefinitionTerminator
- }
- '[' => match t.eat_to_delimiter(']') {
- Some(string) => {
- let constant = ConstantExpression::from_str(&string, t);
- TokenVariant::ConstantExpression(constant)
+ loop {
+ t.eat_whitespace();
+ t.mark_start();
+ let Some(c) = t.eat_char() else { break };
+ let variant = match c {
+ ':' => SynVar::Separator,
+ '{' => SynVar::BlockOpen,
+ '}' => SynVar::BlockClose,
+ '@' => match &self.macro_name {
+ Some(_) => {
+ t.eat_token();
+ SynVar::Error(SynErr::LabelInMacroDefinition)
+ }
+ None => {
+ self.label_name = t.eat_token();
+ SynVar::LabelDefinition(self.label_name.clone())
+ }
}
- None => TokenVariant::Error(ParseError::UnterminatedConstantExpression),
- }
- '{' => TokenVariant::BlockOpen,
- '}' => TokenVariant::BlockClose,
- '(' => match t.eat_to_delimiter(')') {
- Some(string) => TokenVariant::Comment(string),
- None => TokenVariant::Error(ParseError::UnterminatedComment),
- }
- '#' => {
- let token = t.eat_token();
- let pbl = PackedBinaryLiteral::from_str(&token, t);
- TokenVariant::PackedBinaryLiteral(pbl)
- },
- '~' => {
- let token = t.eat_token();
- TokenVariant::Symbol(format!("{}/{token}", self.label_name))
- }
- ':' => TokenVariant::Separator,
- c => {
- let token = format!("{c}{}", t.eat_token());
- if let Some(hex_string) = token.strip_prefix("0x") {
- match usize::from_str_radix(hex_string, 16) {
- Ok(hex) => TokenVariant::HexadecimalLiteral(hex),
- Err(_) => TokenVariant::Error(ParseError::InvalidHexadecimalLiteral(token)),
+ '&' => match &self.macro_name {
+ Some(macro_name) => {
+ let label_name = format!("{macro_name}:{}", t.eat_token());
+ SynVar::LabelDefinition(label_name)
}
- } else {
- match usize::from_str_radix(&token, 10) {
- Ok(value) => TokenVariant::DecimalLiteral(value),
- Err(_) => TokenVariant::Symbol(token),
+ None => {
+ let label_name = &self.label_name;
+ let sublabel_name = format!("{label_name}/{}", t.eat_token());
+ SynVar::LabelDefinition(sublabel_name)
}
}
- }
- };
-
- // Parse source path comments.
- if let TokenVariant::Comment(comment) = &variant {
- // Check if the comment fills the entire line.
- if t.start_position.column == 0 && t.end_of_line() {
- if let Some(path) = comment.strip_prefix(": ") {
- t.embedded_path = Some(PathBuf::from(path.trim()));
- t.embedded_first_line = t.start_position.line + 1;
+ '%' => {
+ let macro_name = t.eat_token();
+ self.macro_name = Some(macro_name.clone());
+ SynVar::MacroDefinition(macro_name)
+ }
+ ';' => {
+ self.macro_name = None;
+ SynVar::MacroDefinitionTerminator
}
- }
+ '[' => {
+ t.mark_child();
+ match t.eat_to_delimiter(']') {
+ Some(_) => {
+ let child = t.subtokenise();
+ t.mark_end();
+ let expr = parse_constant_expression(child, t.get_source());
+ SynVar::Expression(expr)
+ }
+ None => SynVar::Error(SynErr::UnterminatedExpression),
+ }
+ }
+ '(' => match t.eat_to_delimiter(')') {
+ Some(string) => {
+ // Check if the comment fills the entire line.
+ if t.start.position.column == 0 && t.end_of_line() {
+ if let Some(path) = string.strip_prefix(": ") {
+ t.embedded_path = Some(PathBuf::from(path.trim()));
+ t.embedded_first_line = t.start.position.line + 1;
+ }
+ }
+ continue;
+ },
+ None => SynVar::Error(SynErr::UnterminatedComment),
+ }
+ '|' => {
+ let token = t.eat_token();
+ if let Some(hex_string) = token.strip_prefix("0x") {
+ match usize::from_str_radix(hex_string, 16) {
+ Ok(addr) => SynVar::PinnedAddress(addr),
+ Err(_) => SynVar::Error(SynErr::InvalidHexadecimalLiteral(token)),
+ }
+ } else {
+ match usize::from_str_radix(&token, 10) {
+ Ok(addr) => SynVar::PinnedAddress(addr),
+ Err(_) => SynVar::Error(SynErr::InvalidDecimalLiteral(token)),
+ }
+ }
+ }
+ '#' => {
+ t.mark_child();
+ t.eat_token();
+ let pbl = parse_packed_binary_literal(t.subtokenise(), t.get_source());
+ SynVar::PackedBinaryLiteral(pbl)
+ },
+ '~' => match &self.macro_name {
+ Some(macro_name) => {
+ let symbol_name = format!("{macro_name}:{}", t.eat_token());
+ SynVar::Symbol(symbol_name)
+ }
+ None => {
+ let label_name = &self.label_name;
+ let symbol_name = format!("{label_name}/{}", t.eat_token());
+ SynVar::Symbol(symbol_name)
+ }
+ }
+ c => {
+ let token = format!("{c}{}", t.eat_token());
+ if let Some(hex_string) = token.strip_prefix("0x") {
+ match usize::from_str_radix(hex_string, 16) {
+ Ok(value) => SynVar::IntegerLiteral(value as isize),
+ Err(_) => SynVar::Error(SynErr::InvalidHexadecimalLiteral(token)),
+ }
+ } else {
+ match usize::from_str_radix(&token, 10) {
+ Ok(value) => SynVar::IntegerLiteral(value as isize),
+ Err(_) => SynVar::Symbol(token),
+ }
+ }
+ }
+ };
+
+ t.mark_end();
+ let source = t.get_source();
+ self.tokens.push(SyntacticToken { source, variant });
}
- let source = t.mark_end_position();
- Some( Token { source, variant } )
+ return self.tokens;
}
}
diff --git a/src/report.rs b/src/report.rs
new file mode 100644
index 0000000..2acdddc
--- /dev/null
+++ b/src/report.rs
@@ -0,0 +1,229 @@
+use crate::*;
+
+
+static mut ERROR_REPORTED: bool = false;
+
+macro_rules! report_source_error {
+ ($context:expr, $message:expr) => {
+ report_source_issue(LogLevel::Error, $context, $message);
+ unsafe { ERROR_REPORTED = true; }
+ };
+}
+
+macro_rules! exit_if_error_reported {
+ () => {
+ if unsafe { ERROR_REPORTED } {
+ std::process::exit(1);
+ }
+ };
+}
+
+pub fn report_syntactic_errors(syntactic_tokens: &[SyntacticToken], source_code: &str) {
+ use SyntacticTokenVariant as SynVar;
+ for token in syntactic_tokens {
+ let context = Context { source_code: &source_code, source: &token.source };
+ match &token.variant {
+ SynVar::Expression(expr) => for t in &expr.tokens {
+ let context = Context { source_code: &source_code, source: &t.source };
+ if let ExpressionTokenVariant::Error(err) = &t.variant {
+ let ExpressionParseError::InvalidHexadecimalLiteral(hex) = err;
+ let message = format!("Invalid hexadecimal literal {hex:?} in constant expression");
+ report_source_error!(&context, &message);
+ }
+ }
+ SynVar::PackedBinaryLiteral(pbl) => for e in &pbl.errors {
+ let context = Context { source_code: &source_code, source: &e.source };
+ match &e.variant {
+ PackedBinaryLiteralParseErrorVariant::DuplicateFieldName(name) => {
+ let message = format!("Duplicate field name {name:?} in packed binary literal");
+ report_source_error!(&context, &message);
+ }
+ PackedBinaryLiteralParseErrorVariant::InvalidCharacter(c) => {
+ let message = format!("Invalid character {c:?} in packed binary literal");
+ report_source_error!(&context, &message);
+ }
+ }
+ }
+ SynVar::Error(err) => match err {
+ SyntacticParseError::InvalidHexadecimalLiteral(hex) => {
+ let message = format!("Invalid hexadecimal literal {hex:?}");
+ report_source_error!(&context, &message);
+ }
+ SyntacticParseError::InvalidDecimalLiteral(dec) => {
+ let message = format!("Invalid decimal literal {dec:?}");
+ report_source_error!(&context, &message);
+ }
+ SyntacticParseError::InvalidSymbolIdentifier(name) => {
+ let message = format!("Invalid identifier {name:?}");
+ report_source_error!(&context, &message);
+ }
+ SyntacticParseError::UnterminatedComment => {
+ let message = format!("Unterminated comment");
+ report_source_error!(&context, &message);
+ }
+ SyntacticParseError::UnterminatedExpression => {
+ let message = format!("Unterminated constant expression");
+ report_source_error!(&context, &message);
+ }
+ SyntacticParseError::LabelInMacroDefinition => {
+ let message = format!("Only sublabels can be used in macro definitions");
+ report_source_error!(&context, &message);
+ }
+ }
+ _ => (),
+ }
+ }
+ exit_if_error_reported!();
+}
+
+
+pub fn report_semantic_errors(program: &SemanticProgram, source_code: &str) {
+ for (_, definition) in &program.macro_definitions {
+ report_value_errors(&definition.value, source_code);
+ }
+ for token in &program.body {
+ report_semantic_token_errors(token, source_code);
+ }
+ exit_if_error_reported!();
+}
+
+fn report_value_errors(definition: &Value, source_code: &str) {
+ match definition {
+ Value::Integer(integer) => match integer {
+ Integer::Expression(expr) => for token in &expr.tokens {
+ if let ExpressionTokenVariant::Error(error) = &token.variant {
+ let message = match error {
+ ExpressionParseError::InvalidHexadecimalLiteral(hex) =>
+ format!("Invalid hexadecimal literal '{hex}' in constant expression"),
+ };
+ let context = Context { source: &token.source, source_code};
+ report_source_error!(&context, &message);
+ }
+ }
+ _ => (),
+ }
+ Value::Block(block) => {
+ for token in block {
+ report_semantic_token_errors(token, source_code);
+ }
+ }
+ Value::Invocation(invocation) => report_invocation_errors(invocation, source_code),
+ }
+}
+
+fn report_semantic_token_errors(token: &SemanticToken, source_code: &str) {
+ match &token {
+ SemanticToken::Word(pbl) => for error in &pbl.errors {
+ let message = match &error.variant {
+ PackedBinaryLiteralParseErrorVariant::DuplicateFieldName(name) =>
+ format!("Duplicate field name '{name}' in packed binary literal"),
+ PackedBinaryLiteralParseErrorVariant::InvalidCharacter(c) =>
+ format!("Invalid character '{c}' in packed binary literal"),
+ };
+ let context = Context { source: &error.source, source_code };
+ report_source_error!(&context, &message);
+ }
+ SemanticToken::Invocation(invocation) => {
+ report_invocation_errors(invocation, source_code)
+ }
+ SemanticToken::Error(error) => {
+ report_semantic_error(error, source_code)
+ }
+ SemanticToken::LabelDefinition(_) => (),
+ SemanticToken::PinnedAddress(_) => (),
+ }
+}
+
+fn report_invocation_errors(invocation: &Invocation, source_code: &str) {
+ for error in &invocation.errors {
+ report_semantic_error(&error, source_code);
+ }
+ for argument in &invocation.arguments {
+ report_value_errors(&argument.value, source_code);
+ }
+}
+
+fn report_semantic_error(error: &SemanticParseError, source_code: &str) {
+ let message = match &error.variant {
+ SemanticParseErrorVariant::UnterminatedMacroDefinition(name) =>
+ format!("The macro definition '{name}' is missing a terminating ';' character"),
+ SemanticParseErrorVariant::UnterminatedBlock =>
+ format!("Block literal is missing a terminating '}}' character"),
+ SemanticParseErrorVariant::InvalidToken =>
+ format!("Invalid token"),
+ };
+ let context = Context { source: &error.source, source_code};
+ report_source_error!(&context, &message);
+}
+
+
+pub fn report_assembler_errors(tokens: &[AssembledToken], source_code: &str) {
+ for token in tokens {
+ match token {
+ AssembledToken::Word(word) => {
+ for error in &word.errors {
+ report_assembler_error(&error, source_code);
+ }
+ }
+ AssembledToken::Error(error) => {
+ report_assembler_error(error, source_code);
+ },
+ _ => (),
+ }
+ }
+ exit_if_error_reported!();
+}
+
+fn report_assembler_error(error: &AssemblerError, source_code: &str) {
+ let message = match &error.variant {
+ AssemblerErrorVariant::DefinitionNotFound(name) =>
+ format!("Definition not found for name '{name}'"),
+ AssemblerErrorVariant::NotABlock =>
+ format!("Value of type block was expected here"),
+ AssemblerErrorVariant::NotAnInteger =>
+ format!("Value of type integer was expected here"),
+ AssemblerErrorVariant::IntegerInBlock =>
+ format!("Integer in block"),
+ AssemblerErrorVariant::IncorrectArgumentCount(expected, received) =>
+ format!("Expected {expected} arguments, but received {received} instead"),
+ AssemblerErrorVariant::IncorrectArgumentType(expected, received) =>
+ format!("Expected {expected} argument but received {received} instead"),
+ };
+ let context = Context {
+ source_code: &source_code,
+ source: &error.source,
+ };
+ report_source_error!(&context, &message);
+}
+
+
+pub fn report_bytecode_errors(bytecode: &Bytecode, source_code: &str) {
+ for error in &bytecode.errors {
+ report_bytecode_error(error, source_code);
+ }
+ exit_if_error_reported!();
+}
+
+pub fn report_bytecode_error(error: &BytecodeError, source_code: &str) {
+ let message = match &error.variant {
+ BytecodeErrorVariant::DefinitionNotFound(name) =>
+ format!("Could not find definition for label reference '{name}'"),
+ BytecodeErrorVariant::DuplicateLabelDefinition(name) =>
+ format!("Duplicate definition for label '{name}'"),
+ BytecodeErrorVariant::PinnedAddressBacktrack(expected, received) =>
+ format!("Cannot pin back to address {expected} when already at address {received}"),
+ BytecodeErrorVariant::ValueTooLarge(expected, received) =>
+ format!("Expected {expected}-bit value, but received {received}-bit value instead"),
+ BytecodeErrorVariant::StackUnderflow =>
+ format!("Stack underflow when evaluating expression"),
+ BytecodeErrorVariant::NoReturnValue =>
+ format!("No value left on stack when evaluating expression"),
+ BytecodeErrorVariant::MultipleReturnValues =>
+ format!("More than one value left on stack when evaluating expression"),
+ };
+ let context = Context {
+ source_code: &source_code,
+ source: &error.source,
+ };
+ report_source_error!(&context, &message);
+}
diff --git a/src/tokens/assembler.rs b/src/tokens/assembler.rs
new file mode 100644
index 0000000..04ecd38
--- /dev/null
+++ b/src/tokens/assembler.rs
@@ -0,0 +1,140 @@
+use crate::*;
+
+
+#[derive(Clone)]
+pub enum AssembledToken {
+ Word(AssembledWord),
+ LabelDefinition(LabelDefinition),
+ PinnedAddress(PinnedAddress),
+ Error(AssemblerError),
+}
+
+#[derive(Clone)]
+pub struct AssembledWord {
+ pub source: SourceSpan,
+ pub value: usize,
+ pub bits: usize,
+ pub fields: Vec<AssembledField>,
+ pub errors: Vec<AssemblerError>,
+}
+
+#[derive(Clone)]
+pub struct AssembledField {
+ pub source: SourceSpan,
+ pub value: IntegerArgument,
+ /// Length of field in bits
+ pub bits: usize,
+ /// Distance to left-shift field in value
+ pub shift: usize,
+}
+
+#[derive(Clone)]
+pub struct AssembledExpression {
+ pub source: SourceSpan,
+ pub tokens: Vec<AssembledExpressionToken>,
+}
+
+#[derive(Clone)]
+pub enum AssembledExpressionToken {
+ Integer(TrackedInteger),
+ LabelReference(Tracked<String>),
+ Operator(Operator),
+ Expression(Box<AssembledExpression>),
+}
+
+#[derive(Clone)]
+pub enum Argument {
+ Integer(IntegerArgument),
+ Block(Vec<AssembledToken>),
+}
+
+#[derive(Clone)]
+pub enum IntegerArgument {
+ LabelReference(Tracked<String>),
+ Integer(TrackedInteger),
+ Expression(AssembledExpression),
+}
+
+#[derive(Clone)]
+pub struct AssemblerError {
+ pub source: SourceSpan,
+ pub variant: AssemblerErrorVariant,
+}
+
+#[derive(Clone, Debug)]
+pub enum AssemblerErrorVariant {
+ DefinitionNotFound(String),
+ NotAnInteger,
+ NotABlock,
+ IntegerInBlock,
+ /// expected, received
+ IncorrectArgumentCount(usize, usize),
+ /// expected, received, index
+ IncorrectArgumentType(ArgumentVariant, ArgumentVariant),
+}
+
+// ------------------------------------------------------------------------ //
+
+macro_rules! indent {
+ ($indent:expr => $($tokens:tt)*) => {{
+ for _ in 0..$indent { print!(" "); }
+ println!($($tokens)*);
+ }};
+}
+
+pub fn print_assembled_tokens(tokens: &[AssembledToken]) {
+ println!();
+ println!("--------------------------------------------------------------");
+ println!();
+ for token in tokens {
+ match token {
+ AssembledToken::LabelDefinition(definition) => {
+ println!("LABEL {}", definition.name)
+ }
+ AssembledToken::PinnedAddress(address) => {
+ println!("PINNED {}", address.address)
+ }
+ AssembledToken::Word(word) => {
+ println!("WORD {:b}", word.value);
+ for field in &word.fields {
+ print!(" FIELD ({} << {}) ", field.bits, field.shift);
+ match &field.value {
+ IntegerArgument::LabelReference(name) => {
+ println!("LABEL '{name}'");
+ }
+ IntegerArgument::Integer(integer) => {
+ println!("INTEGER '{}'", integer.value);
+ }
+ IntegerArgument::Expression(expr) => {
+ println!("EXPRESSION");
+ print_assembled_expression(2, expr);
+ }
+ }
+ }
+ }
+ AssembledToken::Error(error) => {
+ println!("ERROR {:?}", error.variant)
+ }
+ }
+ }
+}
+
+fn print_assembled_expression(indent: usize, expr: &AssembledExpression) {
+ for token in &expr.tokens {
+ match token {
+ AssembledExpressionToken::Integer(integer) => {
+ indent!(indent => "INTEGER {}", integer.value)
+ }
+ AssembledExpressionToken::LabelReference(name) => {
+ indent!(indent => "LABEL '{name}'")
+ }
+ AssembledExpressionToken::Operator(operator) => {
+ indent!(indent => "OPERATOR {operator:?}")
+ }
+ AssembledExpressionToken::Expression(expr) => {
+ indent!(indent => "EXPRESSION");
+ print_assembled_expression(indent+1, expr);
+ }
+ }
+ }
+}
diff --git a/src/tokens/bytecode.rs b/src/tokens/bytecode.rs
new file mode 100644
index 0000000..9ac340e
--- /dev/null
+++ b/src/tokens/bytecode.rs
@@ -0,0 +1,49 @@
+use crate::*;
+
+
+pub struct Bytecode {
+ pub words: Vec<Word>,
+ pub errors: Vec<BytecodeError>,
+}
+
+#[derive(Clone, Copy)]
+pub struct Word {
+ pub bits: usize,
+ pub value: usize,
+}
+
+impl std::fmt::Display for Word {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
+ for i in (0..self.bits).rev() {
+ let is_first_bit = i+1 == self.bits;
+ if !is_first_bit && (i+1) % 4 == 0 {
+ write!(f, "_")?;
+ }
+ match (self.value >> i) & 1 {
+ 0 => write!(f, "0")?,
+ _ => write!(f, "1")?,
+ }
+ }
+ if self.bits == 0 {
+ write!(f, "0")?;
+ }
+ return Ok(());
+ }
+}
+
+pub struct BytecodeError {
+ pub source: SourceSpan,
+ pub variant: BytecodeErrorVariant,
+}
+
+pub enum BytecodeErrorVariant {
+ DefinitionNotFound(String),
+ DuplicateLabelDefinition(String),
+ /// pin, real
+ PinnedAddressBacktrack(usize, usize),
+ /// expected, received
+ ValueTooLarge(usize, usize),
+ StackUnderflow,
+ MultipleReturnValues,
+ NoReturnValue,
+}
diff --git a/src/tokens/constant_expression.rs b/src/tokens/constant_expression.rs
deleted file mode 100644
index e4aa099..0000000
--- a/src/tokens/constant_expression.rs
+++ /dev/null
@@ -1,134 +0,0 @@
-use crate::*;
-
-
-pub struct ConstantExpression {
- pub tokens: Vec<ConstantExpressionToken>,
-}
-
-impl ConstantExpression {
- pub fn from_str(string: &str, tokeniser: &Tokeniser) -> Self {
- parse_constant_expression(string, tokeniser)
- }
-}
-
-pub struct ConstantExpressionToken {
- pub source: SourceSpan,
- pub variant: ConstantExpressionTokenVariant,
-}
-
-pub enum ConstantExpressionTokenVariant {
- SymbolReference(String),
- IntegerLiteral(usize),
- Operator(Operator),
- Error(ConstantExpressionParseError),
-}
-
-pub enum Operator {
- Equal,
- NotEqual,
- LessThan,
- GreaterThan,
- Add,
- Subtract,
- LeftShift,
- RightShift,
- And,
- Or,
- Xor,
- Not,
-}
-
-pub enum ConstantExpressionParseError {
- InvalidHexadecimalLiteral(String),
-}
-
-
-impl ConstantExpression {
- pub fn evaluate(&self, environment: &Environment) -> Result<usize, ConstantExpressionEvaluationError> {
- use ConstantExpressionTokenVariant as Token;
- use ConstantExpressionEvaluationError as EvalErr;
-
- let mut stack = Vec::new();
- macro_rules! push {
- ($value:expr) => { stack.push($value) };
- }
- macro_rules! pop {
- ($name:ident) => { let $name = match stack.pop() {
- Some(value) => value,
- None => return Err(EvalErr::StackUnderflow),
- }; };
- }
- macro_rules! truth {
- ($bool:expr) => { match $bool { true => 1, false => 0 } };
- }
-
- for token in &self.tokens {
- match &token.variant {
- Token::IntegerLiteral(value) => push!(*value),
- Token::SymbolReference(name) => match environment.get_integer(name) {
- Ok(value) => push!(value),
- Err(_) => todo!(),
- }
- Token::Operator(operator) => match operator {
- Operator::Equal => { pop!(b); pop!(a); push!(truth!(a==b)) },
- Operator::NotEqual => { pop!(b); pop!(a); push!(truth!(a!=b)) },
- Operator::LessThan => { pop!(b); pop!(a); push!(truth!(a < b)) },
- Operator::GreaterThan => { pop!(b); pop!(a); push!(truth!(a > b)) },
- Operator::Add => { pop!(b); pop!(a); push!(a + b) },
- Operator::Subtract => { pop!(b); pop!(a); push!(a - b) },
- Operator::LeftShift => { pop!(b); pop!(a); push!(a << b) },
- Operator::RightShift => { pop!(b); pop!(a); push!(a >> b) },
- Operator::And => { pop!(b); pop!(a); push!(a & b) },
- Operator::Or => { pop!(b); pop!(a); push!(a | b) },
- Operator::Xor => { pop!(b); pop!(a); push!(a ^ b) },
- Operator::Not => { pop!(a); push!(!a) },
- }
- Token::Error(_) => (),
- }
- }
- match stack.len() {
- 0 => Err(EvalErr::NoReturnValue),
- 1 => Ok(stack[0]),
- _ => Err(EvalErr::MultipleReturnValues),
- }
- }
-}
-
-pub enum ConstantExpressionEvaluationError {
- StackUnderflow,
- MultipleReturnValues,
- NoReturnValue,
-}
-
-
-impl std::fmt::Debug for ConstantExpression {
- fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
- use ConstantExpressionTokenVariant as TokenVar;
- for (i, token) in self.tokens.iter().enumerate() {
- let string = match &token.variant {
- TokenVar::SymbolReference(name) => name,
- TokenVar::IntegerLiteral(value) => &value.to_string(),
- TokenVar::Operator(operator) => match operator {
- Operator::Equal => "=",
- Operator::NotEqual => "!",
- Operator::LessThan => "<",
- Operator::GreaterThan => ">",
- Operator::Add => "+",
- Operator::Subtract => "-",
- Operator::LeftShift => "<<",
- Operator::RightShift => ">>",
- Operator::And => "&",
- Operator::Or => "|",
- Operator::Xor => "^",
- Operator::Not => "~",
- }
- TokenVar::Error(_) => "<error>",
- };
- match i {
- 0 => write!(f, "{string}")?,
- _ => write!(f, " {string}")?,
- }
- }
- return Ok(());
- }
-}
diff --git a/src/tokens/expression.rs b/src/tokens/expression.rs
new file mode 100644
index 0000000..ff2d82d
--- /dev/null
+++ b/src/tokens/expression.rs
@@ -0,0 +1,74 @@
+use crate::*;
+
+
+#[derive(Clone)]
+pub struct Expression {
+ pub source: SourceSpan,
+ pub tokens: Vec<ExpressionToken>,
+}
+
+#[derive(Clone)]
+pub struct ExpressionToken {
+ pub source: SourceSpan,
+ pub variant: ExpressionTokenVariant,
+}
+
+#[derive(Clone)]
+pub enum ExpressionTokenVariant {
+ Invocation(String),
+ Literal(isize),
+ Operator(Operator),
+ Error(ExpressionParseError),
+}
+
+#[derive(Clone, Copy, Debug)]
+pub enum Operator {
+ Equal,
+ NotEqual,
+ LessThan,
+ GreaterThan,
+ Add,
+ Subtract,
+ LeftShift,
+ RightShift,
+ And,
+ Or,
+ Xor,
+ Not,
+}
+
+#[derive(Clone)]
+pub enum ExpressionParseError {
+ InvalidHexadecimalLiteral(String),
+}
+
+impl std::fmt::Debug for Expression {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
+ for (i, token) in self.tokens.iter().enumerate() {
+ let string = match &token.variant {
+ ExpressionTokenVariant::Invocation(name) => name,
+ ExpressionTokenVariant::Literal(value) => &value.to_string(),
+ ExpressionTokenVariant::Operator(operator) => match operator {
+ Operator::Equal => "=",
+ Operator::NotEqual => "!=",
+ Operator::LessThan => "<",
+ Operator::GreaterThan => ">",
+ Operator::Add => "+",
+ Operator::Subtract => "-",
+ Operator::LeftShift => "<<",
+ Operator::RightShift => ">>",
+ Operator::And => "&",
+ Operator::Or => "|",
+ Operator::Xor => "^",
+ Operator::Not => "~",
+ }
+ ExpressionTokenVariant::Error(_) => "<error>",
+ };
+ match i {
+ 0 => write!(f, "{string}")?,
+ _ => write!(f, " {string}")?,
+ }
+ }
+ return Ok(());
+ }
+}
diff --git a/src/tokens/mod.rs b/src/tokens/mod.rs
index edb7c19..53ccc6e 100644
--- a/src/tokens/mod.rs
+++ b/src/tokens/mod.rs
@@ -1,9 +1,19 @@
-pub mod syntactic;
+mod expression;
+mod packed_binary_literal;
+mod tracked_integer;
+mod tracked;
-pub mod semantic;
+pub use expression::*;
+pub use packed_binary_literal::*;
+pub use tracked_integer::*;
+pub use tracked::*;
-mod constant_expression;
-pub use constant_expression::*;
+mod syntactic;
+mod semantic;
+mod assembler;
+mod bytecode;
-mod packed_binary_literal;
-pub use packed_binary_literal::*;
+pub use syntactic::*;
+pub use semantic::*;
+pub use assembler::*;
+pub use bytecode::*;
diff --git a/src/tokens/packed_binary_literal.rs b/src/tokens/packed_binary_literal.rs
index 1252398..a2720b7 100644
--- a/src/tokens/packed_binary_literal.rs
+++ b/src/tokens/packed_binary_literal.rs
@@ -2,17 +2,13 @@ use crate::*;
pub struct PackedBinaryLiteral {
+ pub source: SourceSpan,
pub value: usize,
+ pub bits: usize,
pub fields: Vec<BitField>,
pub errors: Vec<PackedBinaryLiteralParseError>,
}
-impl PackedBinaryLiteral {
- pub fn from_str(string: &str, parent: &Tokeniser) -> Self {
- parse_packed_binary_literal(string, parent)
- }
-}
-
pub struct BitField {
pub name: char,
pub source: SourceSpan,
@@ -40,7 +36,8 @@ impl std::fmt::Display for PackedBinaryLiteral {
} else {
let bitcount = (self.value.ilog2() + 1) as usize;
'bit: for i in (0..bitcount).rev() {
- if (i+1) % 4 == 0 {
+ let is_first_bit = i+1 == bitcount;
+ if !is_first_bit && (i+1) % 4 == 0 {
write!(f, "_")?;
}
for field in &self.fields {
diff --git a/src/tokens/semantic.rs b/src/tokens/semantic.rs
index 7d5d327..d61ad8e 100644
--- a/src/tokens/semantic.rs
+++ b/src/tokens/semantic.rs
@@ -1,90 +1,100 @@
use crate::*;
+use indexmap::IndexMap;
+
/// The entire semantic program, ready to generate bytecode.
-pub struct Program {
- pub definitions: Vec<Definition>,
- pub invocations: Vec<Invocation>,
- pub errors: Vec<ParseError>,
+pub struct SemanticProgram {
+ pub macro_definitions: IndexMap<String, MacroDefinition>,
+ pub label_definitions: IndexMap<String, LabelDefinition>,
+ pub body: Vec<SemanticToken>,
}
/// A symbol definition.
-pub struct Definition {
- pub name: String,
+pub struct MacroDefinition {
pub source: SourceSpan,
pub arguments: Vec<ArgumentDefinition>,
- pub variant: DefinitionVariant,
+ pub value: Value,
+ pub errors: Vec<SemanticParseError>,
}
pub struct ArgumentDefinition {
pub name: String,
pub source: SourceSpan,
- pub variant: ArgumentDefinitionVariant,
+ pub variant: ArgumentVariant,
}
-pub enum ArgumentDefinitionVariant {
+#[derive(PartialEq, Clone, Copy, Debug)]
+pub enum ArgumentVariant {
Integer,
Block,
}
-pub enum DefinitionVariant {
- Integer(IntegerDefinition),
- Block(BlockDefinition),
- Reference(ReferenceDefinition),
-}
-
-pub struct IntegerDefinition {
+pub struct ArgumentInvocation {
pub source: SourceSpan,
- pub variant: IntegerDefinitionVariant,
+ pub value: Value,
}
-pub enum IntegerDefinitionVariant {
- Literal(usize),
- Constant(ConstantExpression),
+pub enum Value {
+ Integer(Integer),
+ Block(Vec<SemanticToken>),
+ Invocation(Invocation),
}
-pub struct BlockDefinition {
- pub tokens: Vec<BlockToken>,
- pub errors: Vec<ParseError>,
+pub enum Integer {
+ Literal(TrackedInteger),
+ Expression(Expression),
+ LabelReference(Tracked<String>),
}
-pub struct BlockToken {
- pub source: SourceSpan,
- pub variant: BlockTokenVariant,
+pub enum SemanticToken {
+ Word(PackedBinaryLiteral),
+ Invocation(Invocation),
+ LabelDefinition(LabelDefinition),
+ PinnedAddress(PinnedAddress),
+ Error(SemanticParseError),
}
-pub enum BlockTokenVariant {
- Invocation(Invocation),
- Comment(String),
- Word(PackedBinaryLiteral),
+pub struct Invocation {
+ pub name: String,
+ pub source: SourceSpan,
+ pub arguments: Vec<ArgumentInvocation>,
+ pub errors: Vec<SemanticParseError>,
}
-/// References aren't necessarily an integer or a block
-pub struct ReferenceDefinition {
+#[derive(Clone)]
+pub struct LabelDefinition {
pub source: SourceSpan,
pub name: String,
}
-pub struct Invocation {
- pub name: String,
- pub arguments: Vec<DefinitionVariant>,
- pub errors: Vec<ParseError>,
+#[derive(Clone)]
+pub struct PinnedAddress {
+ pub source: SourceSpan,
+ pub address: usize,
}
-pub struct ParseError {
+pub struct SemanticParseError {
pub source: SourceSpan,
- pub variant: ParseErrorVariant,
+ pub variant: SemanticParseErrorVariant,
}
-pub enum ParseErrorVariant {
+pub enum SemanticParseErrorVariant {
UnterminatedMacroDefinition(String),
- UnterminatedBlockDefinition,
- /// Name of the macro.
- InvalidArgumentDefinition(String),
+ UnterminatedBlock,
InvalidToken,
}
+impl std::fmt::Display for ArgumentVariant {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
+ match self {
+ ArgumentVariant::Integer => write!(f, "integer"),
+ ArgumentVariant::Block => write!(f, "block"),
+ }
+ }
+}
+
// ------------------------------------------------------------------------ //
macro_rules! indent {
@@ -94,77 +104,85 @@ macro_rules! indent {
}};
}
-impl Program {
+impl SemanticProgram {
pub fn print_definitions(&self) {
- for definition in &self.definitions {
- let variant = match &definition.variant {
- DefinitionVariant::Integer(_) => "INTEGER",
- DefinitionVariant::Block(_) => "BLOCK",
- DefinitionVariant::Reference(_) => "REFERENCE",
+ for (name, definition) in &self.macro_definitions {
+ let variant = match &definition.value {
+ Value::Integer(_) => "INTEGER",
+ Value::Block(_) => "BLOCK",
+ Value::Invocation(_) => "INVOCATION",
};
- println!("DEFINE {variant} '{}'", definition.name);
+ println!("DEFINE {variant} '{name}'");
for argument in &definition.arguments {
self.print_argument_definition(argument);
}
- match &definition.variant {
- DefinitionVariant::Integer(integer) =>
- self.print_integer_definition(1, integer),
- DefinitionVariant::Block(block) =>
- self.print_block_definition(1, block),
- DefinitionVariant::Reference(reference) =>
- indent!(1 => "REFERENCE '{}'", reference.name),
+ match &definition.value {
+ Value::Integer(integer) =>
+ self.print_integer(1, integer),
+ Value::Block(block) =>
+ self.print_block(1, block),
+ Value::Invocation(invocation) =>
+ indent!(1 => "INVOCATION '{}'", invocation.name),
};
println!();
}
- for invocation in &self.invocations {
- self.print_invocation(0, invocation);
+ println!("LABELS");
+ for (name, _) in &self.label_definitions {
+ println!(" @{name}");
}
+ println!();
+
+ self.print_block(0, &self.body);
}
fn print_argument_definition(&self, argument: &ArgumentDefinition) {
let variant = match argument.variant {
- ArgumentDefinitionVariant::Integer => "INTEGER",
- ArgumentDefinitionVariant::Block => "BLOCK",
+ ArgumentVariant::Integer => "INTEGER",
+ ArgumentVariant::Block => "BLOCK",
};
println!(" ARGUMENT {variant} '{}'", argument.name);
}
- fn print_integer_definition(&self, indent: usize, definition: &IntegerDefinition) {
- match &definition.variant {
- IntegerDefinitionVariant::Literal(value) =>
+ fn print_integer(&self, indent: usize, integer: &Integer) {
+ match &integer {
+ Integer::Literal(value) =>
indent!(indent => "LITERAL {value}"),
- IntegerDefinitionVariant::Constant(expr) =>
- indent!(indent => "CONSTANT [{expr:?}]"),
+ Integer::Expression(expr) =>
+ indent!(indent => "EXPRESSION [{expr:?}]"),
+ Integer::LabelReference(name) =>
+ indent!(indent => "LABEL REFERENCE '{name}'"),
}
}
- fn print_block_definition(&self, indent: usize, definition: &BlockDefinition) {
+ fn print_block(&self, indent: usize, block: &[SemanticToken]) {
indent!(indent => "BLOCK");
- let indent = indent + 1;
- for token in &definition.tokens {
- match &token.variant {
- BlockTokenVariant::Invocation(invocation) =>
- self.print_invocation(indent, invocation),
- BlockTokenVariant::Comment(_) =>
- indent!(indent => "COMMENT"),
- BlockTokenVariant::Word(word) =>
- indent!(indent => "WORD #{word}"),
+ for semantic_token in block {
+ match &semantic_token {
+ SemanticToken::Word(word) =>
+ indent!(indent+1 => "WORD #{word}"),
+ SemanticToken::Invocation(invocation) =>
+ self.print_invocation(indent+1, invocation),
+ SemanticToken::LabelDefinition(definition) =>
+ indent!(indent+1 => "LABEL DEFINITION @{}", definition.name),
+ SemanticToken::PinnedAddress(addr) =>
+ indent!(indent+1 => "PINNED ADDRESS {}", addr.address),
+ SemanticToken::Error(_) =>
+ indent!(indent+1 => "ERROR"),
}
}
}
fn print_invocation(&self, indent: usize, invocation: &Invocation) {
indent!(indent => "INVOCATION '{}'", invocation.name);
- let indent = indent + 1;
for argument in &invocation.arguments {
- match &argument {
- DefinitionVariant::Integer(integer) =>
- self.print_integer_definition(indent, integer),
- DefinitionVariant::Block(block) =>
- self.print_block_definition(indent, block),
- DefinitionVariant::Reference(reference) =>
- indent!(indent => "REFERENCE '{}'", reference.name),
+ match &argument.value {
+ Value::Integer(integer) =>
+ self.print_integer(indent+1, integer),
+ Value::Block(block) =>
+ self.print_block(indent+1, block),
+ Value::Invocation(invocation) =>
+ self.print_invocation(indent+1, invocation),
};
}
}
diff --git a/src/tokens/syntactic.rs b/src/tokens/syntactic.rs
index 162f1c0..eb33806 100644
--- a/src/tokens/syntactic.rs
+++ b/src/tokens/syntactic.rs
@@ -1,22 +1,21 @@
use crate::*;
-pub struct Token {
+pub struct SyntacticToken {
pub source: SourceSpan,
- pub variant: TokenVariant,
+ pub variant: SyntacticTokenVariant,
}
-pub enum TokenVariant {
+pub enum SyntacticTokenVariant {
LabelDefinition(String),
MacroDefinition(String),
MacroDefinitionTerminator,
- DecimalLiteral(usize),
- HexadecimalLiteral(usize),
+ IntegerLiteral(isize),
PackedBinaryLiteral(PackedBinaryLiteral),
+ PinnedAddress(usize),
- Comment(String),
- ConstantExpression(ConstantExpression),
+ Expression(Expression),
BlockOpen,
BlockClose,
@@ -24,33 +23,34 @@ pub enum TokenVariant {
Symbol(String),
- Error(ParseError),
+ Error(SyntacticParseError),
}
#[derive(Debug)]
-pub enum ParseError {
+pub enum SyntacticParseError {
InvalidHexadecimalLiteral(String),
+ InvalidDecimalLiteral(String),
InvalidSymbolIdentifier(String),
UnterminatedComment,
- UnterminatedConstantExpression,
+ UnterminatedExpression,
+ LabelInMacroDefinition,
}
-impl std::fmt::Debug for Token {
+impl std::fmt::Debug for SyntacticToken {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
- use TokenVariant::*;
+ use SyntacticTokenVariant::*;
let start = &self.source.in_merged;
let name = match &self.variant {
LabelDefinition(name) => format!("LabelDefinition({name})"),
MacroDefinition(name) => format!("MacroDefinition({name})"),
MacroDefinitionTerminator => format!("MacroDefinitionTerminator"),
- DecimalLiteral(value) => format!("DecimalLiteral({value})"),
- HexadecimalLiteral(value) => format!("HexadecimalLiteral(0x{value:x})"),
+ IntegerLiteral(value) => format!("IntegerLiteral({value})"),
PackedBinaryLiteral(pbl) => format!("PackedBinaryLiteral({pbl})"),
+ PinnedAddress(value) => format!("PinnedAddress({value})"),
- Comment(_) => format!("Comment"),
- ConstantExpression(expr) => format!("ConstantExpression({expr:?})"),
+ Expression(expr) => format!("Expression({expr:?})"),
BlockOpen => format!("BlockOpen"),
BlockClose => format!("BlockClose"),
diff --git a/src/tokens/tracked.rs b/src/tokens/tracked.rs
new file mode 100644
index 0000000..049c8f8
--- /dev/null
+++ b/src/tokens/tracked.rs
@@ -0,0 +1,47 @@
+use crate::*;
+
+
+#[derive(Clone)]
+pub struct Tracked<T> {
+ pub source: SourceSpan,
+ pub value: T,
+}
+
+impl<T> Tracked<T> {
+ pub fn from(value: T, source: &SourceSpan) -> Self {
+ Self { source: source.clone(), value }
+ }
+}
+
+impl<T> std::ops::Deref for Tracked<T> {
+ type Target = T;
+ fn deref(&self) -> &T {
+ &self.value
+ }
+}
+
+impl<T> std::ops::DerefMut for Tracked<T> {
+ fn deref_mut(&mut self) -> &mut T {
+ &mut self.value
+ }
+}
+
+impl<T: std::fmt::Display> std::fmt::Display for Tracked<T> {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
+ write!(f, "{}", self.value)
+ }
+}
+
+impl<T: std::fmt::Debug> std::fmt::Debug for Tracked<T> {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
+ write!(f, "{:?}", self.value)
+ }
+}
+
+impl<T: PartialEq> PartialEq for Tracked<T> {
+ fn eq(&self, other: &Tracked<T>) -> bool {
+ self.value.eq(&other.value)
+ }
+}
+
+impl<T: Eq> Eq for Tracked<T> {}
diff --git a/src/tokens/tracked_integer.rs b/src/tokens/tracked_integer.rs
new file mode 100644
index 0000000..fa55f09
--- /dev/null
+++ b/src/tokens/tracked_integer.rs
@@ -0,0 +1,14 @@
+use crate::*;
+
+
+#[derive(Clone)]
+pub struct TrackedInteger {
+ pub source: SourceSpan,
+ pub value: isize,
+}
+
+impl std::fmt::Display for TrackedInteger {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
+ write!(f, "{}", self.value)
+ }
+}