Browse Source

Basic parser done

pull/1/head
Stephen 1 week ago
committed by Stephen
parent
commit
e0daa809a1
11 changed files with 548 additions and 7 deletions
  1. +2
    -2
      example.es
  2. +26
    -0
      src/electro_program.rs
  3. +15
    -0
      src/error_handler.rs
  4. +12
    -0
      src/external_module.rs
  5. +19
    -0
      src/internal_module.rs
  6. +10
    -1
      src/main.rs
  7. +71
    -0
      src/module_param.rs
  8. +296
    -0
      src/parser.rs
  9. +20
    -2
      src/scanner.rs
  10. +75
    -0
      src/stmt.rs
  11. +2
    -2
      src/token.rs

+ 2
- 2
example.es View File

@ -5,7 +5,7 @@ extern module xor("xor.sch");
# a binary half adder
module half_adder(input a, input b, output sum, output carryOut)
{
xor(a, b, output);
xor(a, b, sum);
and(a, b, carryOut);
}
@ -52,7 +52,7 @@ expect and(0, 1, 0);
expect and(1, 0, 0);
expect and(1, 1, 1); # can also write `expect and(HIGH, HIGH, HIGH)` - HIGH = 1 and LOW = 0; The compiler doesn't care
test adder works # start with "test", then the name of the test case
test "adder works" # start with "test", then the name of the test case
{
signal Sum[0:7];
signal cOut;


+ 26
- 0
src/electro_program.rs View File

@ -0,0 +1,26 @@
use crate::external_module::ExternalModule;
use crate::internal_module::InternalModule;
use std::collections::HashMap;
// Represents the AST of an electroscript file.
pub struct ElectroProgram {
internal_modules: HashMap<String, InternalModule>,
external_modules: HashMap<String, ExternalModule>,
}
impl ElectroProgram {
pub fn new() -> Self {
Self {
internal_modules: HashMap::new(),
external_modules: HashMap::new(),
}
}
pub fn add_external(&mut self, em: ExternalModule) {
self.external_modules.insert(em.name.clone(), em);
}
pub fn add_internal(&mut self, im: InternalModule) {
self.internal_modules.insert(im.name.clone(), im);
}
}

+ 15
- 0
src/error_handler.rs View File

@ -1,3 +1,5 @@
use crate::token::{Token, TokenType};
pub struct ErrorHandler {
pub errors: Vec<String>,
}
@ -11,4 +13,17 @@ impl ErrorHandler {
self.errors
.push(format!("Error on line {}: {}", line, message));
}
pub fn error_token(&mut self, token: &Token, msg: &str) {
if token.token_type == TokenType::Eof {
self.report(token.line, "at end", msg);
} else {
self.report(token.line, &format!("at '{}'", token.lexeme), msg);
}
}
fn report(&mut self, line: usize, location: &str, msg: &str) {
self.errors
.push(format!("[line {}] Error {}: {}", line, location, msg));
}
}

+ 12
- 0
src/external_module.rs View File

@ -0,0 +1,12 @@
use crate::module_param::ModuleParamType;
pub struct ExternalModule {
pub name: String,
pub params: Vec<ModuleParamType>,
}
impl ExternalModule {
pub fn new(name: String, params: Vec<ModuleParamType>) -> Self {
Self { name, params }
}
}

+ 19
- 0
src/internal_module.rs View File

@ -0,0 +1,19 @@
use crate::module_param::ModuleParam;
use crate::stmt::Stmt;
#[derive(Debug)]
pub struct InternalModule {
pub name: String,
params: Vec<ModuleParam>,
stmts: Vec<Stmt>,
}
impl InternalModule {
pub fn new(name: String, params: Vec<ModuleParam>, stmts: Vec<Stmt>) -> Self {
Self {
name,
params,
stmts,
}
}
}

+ 10
- 1
src/main.rs View File

@ -1,7 +1,14 @@
mod electro_program;
mod error_handler;
mod external_module;
mod internal_module;
mod module_param;
mod parser;
mod scanner;
mod stmt;
mod token;
use crate::parser::Parser;
use crate::scanner::Scanner;
use std::env;
use std::fs;
@ -31,7 +38,9 @@ fn process_file(file_name: &str) -> Result<(), Vec<String>> {
.map_err(|err| vec![format!("Could not open {}: {}", file_name, err)])?;
let scanner = Scanner::new(&code);
let tokens = scanner.scan_tokens()?;
println!("{:?}", tokens);
// println!("{:?}", tokens);
let parser = Parser::new(tokens);
parser.parse();
Ok(())
}

+ 71
- 0
src/module_param.rs View File

@ -0,0 +1,71 @@
use crate::token::{Token, TokenType};
#[derive(Debug)]
pub struct BusMetadata {
start_index: i32,
end_index: i32,
}
impl BusMetadata {
pub fn new(start_index: i32, end_index: i32) -> Self {
Self {
start_index,
end_index,
}
}
pub fn len(&self) -> i32 {
self.end_index - self.start_index + 1
}
}
#[derive(Debug)]
pub enum ModuleParamType {
Input,
Output,
InputBus(BusMetadata),
OutputBus(BusMetadata),
}
#[derive(Debug)]
pub struct ModuleParam {
param_type: ModuleParamType,
name: String,
line: usize,
}
impl ModuleParam {
pub fn new(type_token: &Token, name_token: Token) -> Self {
let param_type = match type_token.token_type {
TokenType::Input => ModuleParamType::Input,
TokenType::Output => ModuleParamType::Output,
_ => panic!("Internal compiler error"),
};
Self {
param_type,
name: name_token.lexeme,
line: name_token.line,
}
}
pub fn new_bus(
type_token: &Token,
name_token: Token,
start_index: i32,
end_index: i32,
) -> Self {
let metadata = BusMetadata::new(start_index, end_index);
let param_type = match type_token.token_type {
TokenType::Input => ModuleParamType::InputBus(metadata),
TokenType::Output => ModuleParamType::OutputBus(metadata),
_ => panic!("Internal compiler error"),
};
Self {
param_type,
name: name_token.lexeme,
line: name_token.line,
}
}
}

+ 296
- 0
src/parser.rs View File

@ -0,0 +1,296 @@
use crate::{
electro_program::ElectroProgram,
error_handler::ErrorHandler,
external_module::ExternalModule,
internal_module::InternalModule,
module_param::{ModuleParam, ModuleParamType},
stmt::{Connection, Stmt},
token::{Token, TokenType},
};
pub struct Parser {
tokens: Vec<Token>,
current: usize,
error_handler: ErrorHandler,
}
impl Parser {
pub fn new(tokens: Vec<Token>) -> Self {
Self {
tokens,
current: 0,
error_handler: ErrorHandler::new(),
}
}
pub fn parse(mut self) -> Option<ElectroProgram> {
let mut ep = ElectroProgram::new();
while !self.is_at_end() {
if self.try_parse(&mut ep).is_none() {
self.synchronize();
}
}
println!("{}", self.error_handler.errors.join("\n"));
Some(ep)
}
fn try_parse(&mut self, ep: &mut ElectroProgram) -> Option<()> {
if self.match_type(TokenType::Extern).is_some() {
// external
ep.add_external(self.external()?);
} else if self.match_type(TokenType::Module).is_some() {
// internal module
ep.add_internal(self.intern_module()?);
} else {
self.error_handler
.error_token(&self.peek(), &format!("Unexpected token."));
return None;
}
Some(())
}
fn external(&mut self) -> Option<ExternalModule> {
self.consume(TokenType::Module, "Expected 'module' after 'extern'.")?;
let name = self
.consume(
TokenType::Identifier,
"Expected module name after 'module'.",
)?
.lexeme;
self.consume(TokenType::LeftParen, "Expected '(' after module name.")?;
let filename = self.consume_string("Expected filename for extern module declaration.")?; // TODO load external module kicad file
self.consume(
TokenType::RightParen,
"Expected ')' after extern module filename.",
)?;
self.consume(
TokenType::Semicolon,
"Expected ';' after extern module declaration.",
)?;
// Params are TODO because they need to be read from the kicad file.
// For now, we'll just hardcode it as input, input, output, since that's what we need for logic gates
Some(ExternalModule::new(
name,
vec![
ModuleParamType::Input,
ModuleParamType::Input,
ModuleParamType::Output,
],
))
}
fn intern_module(&mut self) -> Option<InternalModule> {
let name = self
.consume(
TokenType::Identifier,
"Expected module name after 'module'.",
)?
.lexeme;
self.consume(TokenType::LeftParen, "Expected '(' after module name.")?;
// Get parameters
let mut params = Vec::new();
if self.peek().token_type != TokenType::RightParen {
let mut another = true;
while !self.is_at_end() && another {
let param_type = self.consume_any(
&[TokenType::Input, TokenType::Output],
"Expected signal direction declaration in module definition.",
)?;
let param_name = self.consume(TokenType::Identifier, "Expected signal name.")?;
let module_param = if self.match_type(TokenType::LeftBracket).is_some() {
let start_index = self.consume_number("Expected start index for bus.")?;
self.consume(
TokenType::Colon,
"Expected ':' between start and end index.",
)?;
let end_index = self.consume_number("Expected end index for bus.")?;
self.consume(TokenType::RightBracket, "Expected ']'.")?;
ModuleParam::new_bus(&param_type, param_name, start_index, end_index)
} else {
ModuleParam::new(&param_type, param_name)
};
params.push(module_param);
another = self.match_type(TokenType::Comma).is_some();
}
}
self.consume(
TokenType::RightParen,
"Expected ')' after module parameters.",
)?;
self.consume(TokenType::LeftBrace, "Expected '{' before module body.")?;
let mut stmts = Vec::new();
while self.peek().token_type != TokenType::RightBrace {
stmts.push(self.statement()?);
}
self.consume(TokenType::RightBrace, "Expected '}' after module body.");
Some(InternalModule::new(name, params, stmts))
}
fn statement(&mut self) -> Option<Stmt> {
let token = self.advance();
let ret = match token.token_type {
TokenType::Signal => {
let name = self
.consume(TokenType::Identifier, "Expected identifier after 'signal'.")?
.lexeme;
if self.match_type(TokenType::LeftBracket).is_some() {
let start_index = self.consume_number("Expected start index for bus.")?;
self.consume(
TokenType::Colon,
"Expected ':' between start and end index.",
)?;
let end_index = self.consume_number("Expected end index for bus.")?;
self.consume(TokenType::RightBracket, "Expected ']'.")?;
Stmt::new_bus(name, start_index, end_index)
} else {
Stmt::new_signal(name)
}
}
TokenType::Identifier => self.call(token.lexeme)?,
_ => {
self.error_handler.error_token(&token, "Unexpected token.");
return None;
}
};
self.consume(TokenType::Semicolon, "Expected ';' after statement.")?;
Some(ret)
}
fn call(&mut self, name: String) -> Option<Stmt> {
self.consume(TokenType::LeftParen, "Expected '(' after identifier.")?;
let mut args = Vec::new();
if self.peek().token_type != TokenType::RightParen {
let mut another = true;
while !self.is_at_end() && another {
let connection_name = self
.consume(
TokenType::Identifier,
"Expected identifier for module argument.",
)?
.lexeme;
let conn = if self.match_type(TokenType::LeftBracket).is_some() {
let index = self.consume_number("Expected number after '['.")?;
let c = if self.match_type(TokenType::Colon).is_some() {
let end_index = self.consume_number("Expected number after ':'.")?;
Connection::new_bus_from_bus(connection_name, index, end_index)
} else {
Connection::new_signal_from_bus(connection_name, index)
};
self.consume(TokenType::RightBracket, "Expected ']'.")?;
c
} else {
Connection::new_signal_from_signal(connection_name)
};
args.push(conn);
another = self.match_type(TokenType::Comma).is_some();
}
}
self.consume(TokenType::RightParen, "Expected ')' after argument list.")?;
Some(Stmt::new_call(name, args))
}
fn synchronize(&mut self) {
self.advance();
while !self.is_at_end() {
if self.peek().token_type == TokenType::Module {
return;
}
self.advance();
}
}
fn is_at_end(&self) -> bool {
self.peek().token_type == TokenType::Eof
}
fn peek(&self) -> Token {
self.tokens[self.current].clone()
}
fn match_type(&mut self, token_type: TokenType) -> Option<Token> {
if self.check(&token_type) {
Some(self.advance())
} else {
None
}
}
fn check(&self, token_type: &TokenType) -> bool {
if self.is_at_end() {
return false;
}
&self.peek().token_type == token_type
}
fn advance(&mut self) -> Token {
let token = self.peek();
if !self.is_at_end() {
self.current += 1;
}
token
}
fn consume(&mut self, token_type: TokenType, message: &str) -> Option<Token> {
if self.check(&token_type) {
Some(self.advance())
} else {
self.error_handler.error_token(&self.peek(), message);
None
}
}
fn consume_any(&mut self, types: &[TokenType], message: &str) -> Option<Token> {
for t in types {
if self.check(&t) {
return Some(self.advance());
}
}
self.error_handler.error_token(&self.peek(), message);
None
}
fn consume_string(&mut self, message: &str) -> Option<Token> {
if let TokenType::String(_) = self.peek().token_type {
Some(self.advance())
} else {
self.error_handler.error_token(&self.peek(), message);
None
}
}
fn consume_number(&mut self, message: &str) -> Option<i32> {
if let TokenType::Number(num) = self.peek().token_type {
self.advance();
Some(num)
} else {
self.error_handler.error_token(&self.peek(), message);
None
}
}
}

+ 20
- 2
src/scanner.rs View File

@ -87,7 +87,7 @@ impl Scanner {
return Ok(());
}
' ' | '\r' | '\t' => return Ok(()),
'0'..='9' => {
'0'..='9' | '-' => {
self.number();
return Ok(());
}
@ -142,7 +142,7 @@ impl Scanner {
let s: String = self.chars[self.start..self.current].iter().collect();
// Safe to unwrap here because we know s can only contain digits
self.add_token(TokenType::Number(u32::from_str(&s).unwrap()));
self.add_token(TokenType::Number(i32::from_str(&s).unwrap()));
}
fn identifier(&mut self) {
@ -219,4 +219,22 @@ mod test {
println!("{:?}", token_types);
assert_eq!(9, token_types.len());
}
#[test]
fn positive_number() {
let scanner = Scanner::new("3");
assert_eq!(
TokenType::Number(3),
scanner.scan_tokens().unwrap()[0].token_type
);
}
#[test]
fn negative_number() {
let scanner = Scanner::new("-3");
assert_eq!(
TokenType::Number(-3),
scanner.scan_tokens().unwrap()[0].token_type
);
}
}

+ 75
- 0
src/stmt.rs View File

@ -0,0 +1,75 @@
#[derive(Clone, Debug)]
pub enum Connection {
// Also bus from bus where it's a 1:1 mapping
SignalFromSignal {
signal_name: String,
},
SignalFromBus {
bus_name: String,
index: i32,
},
BusFromBus {
bus_name: String,
start_index: i32,
end_index: i32,
},
BusFromLiteral {
literal: i32,
},
}
impl Connection {
pub fn new_signal_from_signal(signal_name: String) -> Self {
Self::SignalFromSignal { signal_name }
}
pub fn new_signal_from_bus(bus_name: String, index: i32) -> Self {
Self::SignalFromBus { bus_name, index }
}
pub fn new_bus_from_bus(bus_name: String, start_index: i32, end_index: i32) -> Self {
Self::BusFromBus {
bus_name,
start_index,
end_index,
}
}
}
#[derive(Clone, Debug)]
pub enum Stmt {
Signal {
name: String,
},
Bus {
name: String,
start_index: i32,
end_index: i32,
},
Call {
name: String,
args: Vec<Connection>,
},
}
impl Stmt {
pub fn new_signal(name: String) -> Self {
Stmt::Signal { name }
}
pub fn new_bus(name: String, start_index: i32, end_index: i32) -> Self {
assert!(start_index <= end_index);
Stmt::Bus {
name,
start_index,
end_index,
}
}
pub fn new_call(name: String, args: Vec<Connection>) -> Self {
Stmt::Call { name, args }
}
}

+ 2
- 2
src/token.rs View File

@ -1,4 +1,4 @@
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq)]
pub enum TokenType {
Extern,
Module,
@ -22,7 +22,7 @@ pub enum TokenType {
Comma,
Semicolon,
Number(u32),
Number(i32),
String(String),
Eof,


Loading…
Cancel
Save