some more structure and grammar parser fixes

This commit is contained in:
Norman Köhring 2025-07-05 12:58:22 +02:00
parent dd42ef23d4
commit 12c0431e69
11 changed files with 1919 additions and 579 deletions

1117
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -4,6 +4,11 @@ version = "0.1.0"
edition = "2024"
[dependencies]
anyhow = "1.0.98"
lazy_static = "1.5.0"
pest = "2.8.1"
pest_derive = "2.8.1"
swc_common = "13.0.2"
swc_ecma_ast = "13.0.0"
swc_ecma_codegen = "15.0.1"
thiserror = "2.0.12"

35
src/emitter/js.rs Normal file
View file

@ -0,0 +1,35 @@
use swc_ecma_ast::Module;
use swc_ecma_codegen::Config;
// use swc_ecma_codegen::{text_writer::JsWriter, Config, Emitter};
// use swc_common::{sync::Lrc, SourceMap};
use thiserror::Error;
#[derive(Error, Debug)]
pub enum EmitError {
#[error("Failed to emit JavaScript: {0}")]
EmitFailed(String),
}
pub struct JsEmitter {
config: Config,
}
impl JsEmitter {
pub fn new() -> Self {
Self {
config: Config::default(),
}
}
pub fn with_minify(mut self) -> Self {
self.config.minify = true;
self
}
pub fn emit(&self, _module: Module) -> Result<String, EmitError> {
// SWC codegen here
return Err(EmitError::EmitFailed(
"Emitter not yet implemented!".to_owned(),
));
}
}

1
src/emitter/mod.rs Normal file
View file

@ -0,0 +1 @@
pub mod js;

View file

@ -1,561 +1,40 @@
use std::fs;
use pest::iterators::{Pair, Pairs};
use pest::pratt_parser::{Assoc, Op, PrattParser};
use pest::Parser;
use pest_derive::Parser;
mod emitter;
mod parser;
mod transformer;
#[derive(Parser)]
#[grammar = "solace.pest"]
pub struct SolaceParser;
use anyhow::Result;
use std::{env, fs};
use thiserror::Error;
#[derive(Debug, Clone)]
pub enum Expr {
// Literals
Number(f64),
String(String),
Boolean(bool),
None,
Undefined,
Underscore,
// Variables and calls
Identifier(String),
MemberAccess(Box<Expr>, String),
Index(Box<Expr>, Box<Expr>),
Call(Box<Expr>, Vec<Expr>),
// Operators
Binary(BinaryOp, Box<Expr>, Box<Expr>),
Unary(UnaryOp, Box<Expr>),
Ternary(Box<Expr>, Box<Expr>, Box<Expr>),
Assignment(String, Box<Expr>),
// Control flow
If(Box<Expr>, Box<Block>, Option<Box<Block>>),
Match(Option<Box<Expr>>, Vec<MatchArm>),
// Collections
Array(Vec<Expr>),
// Postfix
PostIncrement(Box<Expr>),
PostDecrement(Box<Expr>),
#[derive(Error, Debug)]
pub enum ArgumentError {
#[error("Usage: {0} ./path/to/file.solace")]
MissingSourceFile(String),
}
#[derive(Debug, Clone)]
pub enum BinaryOp {
Add, Sub, Mul, Div, Mod,
Eq, Ne, Lt, Gt, Le, Ge,
And, Or,
RangeInclusive, RangeExclusive,
Is,
fn main() -> Result<()> {
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
return Err(ArgumentError::MissingSourceFile(args[0].clone()).into());
}
let file_path = &args[1];
let input = fs::read_to_string(file_path).expect(&format!("Cannot read file '{}'!", file_path));
// Parse Solace Code
let ast = parser::parse(&input, /* debug */ true)?;
// Transform from Solace AST to SWC AST
let js_transformer = transformer::js::JsTransformer::new();
let js_ast = js_transformer.transform(ast);
// Emit JavaScript
let js_emitter = emitter::js::JsEmitter::new();
let js_code = js_emitter.emit(js_ast)?;
// Write Output to stdout
println!("{}", js_code);
Ok(())
}
#[derive(Debug, Clone)]
pub enum UnaryOp {
Not, Neg, PreIncrement, PreDecrement,
}
#[derive(Debug, Clone)]
pub struct MatchArm {
pattern: MatchPattern,
body: Expr,
}
#[derive(Debug, Clone)]
pub enum MatchPattern {
Wildcard,
Expression(Expr),
Condition(Vec<(BinaryOp, Expr)>),
}
#[derive(Debug, Clone)]
pub struct Block {
statements: Vec<Statement>,
}
#[derive(Debug, Clone)]
pub enum ReturnType {
Simple(Type),
Named {
name: String,
type_annotation: Type,
default_value: Option<Expr>,
}
}
#[derive(Debug, Clone)]
pub enum Statement {
Import { name: String, from: String },
Function {
name: String,
params: Vec<Param>,
return_type: Option<ReturnType>,
extends: Option<String>,
body: Block,
},
Variable {
kind: VarKind,
name: String,
type_annotation: Option<Type>,
value: Expr,
},
Defer {
condition: Option<Expr>,
binding: Option<String>,
body: Block,
},
Watch {
target: String,
body: Block,
},
Return(Option<Expr>),
If(Expr, Box<Statement>, Option<Box<Statement>>),
For {
var: String,
index: Option<String>,
iterable: Expr,
body: Box<Statement>,
},
While(Expr, Box<Statement>),
Expression(Expr),
Block(Block),
}
#[derive(Debug, Clone)]
pub enum VarKind {
Const, Var, Live,
}
#[derive(Debug, Clone)]
pub struct Param {
name: String,
type_annotation: Option<Type>,
}
#[derive(Debug, Clone)]
pub enum Type {
Primitive(String),
Array(Box<Type>),
Optional(Box<Type>),
ErrorUnion(Option<String>, Box<Type>),
Named(String),
}
pub struct SolacePrattParser {
pratt: PrattParser<Rule>,
}
impl SolacePrattParser {
pub fn new() -> Self {
let pratt = PrattParser::new()
.op(Op::infix(Rule::assign, Assoc::Right))
.op(Op::infix(Rule::question, Assoc::Right) | Op::infix(Rule::colon, Assoc::Right))
.op(Op::infix(Rule::or, Assoc::Left))
.op(Op::infix(Rule::and, Assoc::Left))
.op(Op::infix(Rule::eq, Assoc::Left) | Op::infix(Rule::ne, Assoc::Left) | Op::infix(Rule::is_kw, Assoc::Left))
.op(Op::infix(Rule::lt, Assoc::Left) | Op::infix(Rule::gt, Assoc::Left) |
Op::infix(Rule::le, Assoc::Left) | Op::infix(Rule::ge, Assoc::Left))
.op(Op::infix(Rule::range_inclusive, Assoc::Left) |
Op::infix(Rule::range_exclusive, Assoc::Left))
.op(Op::infix(Rule::plus, Assoc::Left) | Op::infix(Rule::minus, Assoc::Left))
.op(Op::infix(Rule::multiply, Assoc::Left) | Op::infix(Rule::divide, Assoc::Left) |
Op::infix(Rule::modulo, Assoc::Left))
.op(Op::prefix(Rule::not) | Op::prefix(Rule::minus) |
Op::prefix(Rule::increment) | Op::prefix(Rule::decrement))
.op(Op::postfix(Rule::increment) | Op::postfix(Rule::decrement));
SolacePrattParser { pratt }
}
pub fn parse_expr(&self, pairs: Pairs<Rule>) -> Result<Expr, String> {
self.pratt
.map_primary(|primary| self.parse_primary(primary))
.map_infix(|lhs, op, rhs| self.parse_infix(lhs, op, rhs))
.map_prefix(|op, rhs| self.parse_prefix(op, rhs))
.map_postfix(|lhs, op| self.parse_postfix(lhs, op))
.parse(pairs)
}
fn parse_primary(&self, pair: Pair<Rule>) -> Result<Expr, String> {
match pair.as_rule() {
Rule::number_literal => {
let num = pair.as_str().parse::<f64>()
.map_err(|_| "Invalid number")?;
Ok(Expr::Number(num))
}
Rule::string_literal => {
let s = pair.as_str();
Ok(Expr::String(s[1..s.len()-1].to_string()))
}
Rule::boolean_literal => {
Ok(Expr::Boolean(pair.as_str() == "true"))
}
Rule::none_kw => Ok(Expr::None),
Rule::undefined_kw => Ok(Expr::Undefined),
Rule::underscore => Ok(Expr::Underscore),
Rule::identifier => Ok(Expr::Identifier(pair.as_str().to_string())),
Rule::array_literal => {
let mut elements = vec![];
for inner in pair.into_inner() {
if inner.as_rule() == Rule::expression {
elements.push(self.parse_expr(inner.into_inner())?);
}
}
Ok(Expr::Array(elements))
}
Rule::if_expr => self.parse_if_expr(pair),
Rule::match_expr => self.parse_match_expr(pair),
_ => Err(format!("Unexpected primary: {:?}", pair.as_rule()))
}
}
fn parse_infix(&self, lhs: Result<Expr, String>, op: Pair<Rule>, rhs: Result<Expr, String>) -> Result<Expr, String> {
let lhs = lhs?;
let rhs = rhs?;
match op.as_rule() {
Rule::plus => Ok(Expr::Binary(BinaryOp::Add, Box::new(lhs), Box::new(rhs))),
Rule::minus => Ok(Expr::Binary(BinaryOp::Sub, Box::new(lhs), Box::new(rhs))),
Rule::multiply => Ok(Expr::Binary(BinaryOp::Mul, Box::new(lhs), Box::new(rhs))),
Rule::divide => Ok(Expr::Binary(BinaryOp::Div, Box::new(lhs), Box::new(rhs))),
Rule::modulo => Ok(Expr::Binary(BinaryOp::Mod, Box::new(lhs), Box::new(rhs))),
Rule::eq => Ok(Expr::Binary(BinaryOp::Eq, Box::new(lhs), Box::new(rhs))),
Rule::ne => Ok(Expr::Binary(BinaryOp::Ne, Box::new(lhs), Box::new(rhs))),
Rule::lt => Ok(Expr::Binary(BinaryOp::Lt, Box::new(lhs), Box::new(rhs))),
Rule::gt => Ok(Expr::Binary(BinaryOp::Gt, Box::new(lhs), Box::new(rhs))),
Rule::le => Ok(Expr::Binary(BinaryOp::Le, Box::new(lhs), Box::new(rhs))),
Rule::ge => Ok(Expr::Binary(BinaryOp::Ge, Box::new(lhs), Box::new(rhs))),
Rule::and => Ok(Expr::Binary(BinaryOp::And, Box::new(lhs), Box::new(rhs))),
Rule::or => Ok(Expr::Binary(BinaryOp::Or, Box::new(lhs), Box::new(rhs))),
Rule::is_kw => Ok(Expr::Binary(BinaryOp::Is, Box::new(lhs), Box::new(rhs))),
Rule::range_inclusive => Ok(Expr::Binary(BinaryOp::RangeInclusive, Box::new(lhs), Box::new(rhs))),
Rule::range_exclusive => Ok(Expr::Binary(BinaryOp::RangeExclusive, Box::new(lhs), Box::new(rhs))),
Rule::assign => {
if let Expr::Identifier(name) = lhs {
Ok(Expr::Assignment(name, Box::new(rhs)))
} else {
Err("Left side of assignment must be identifier".to_string())
}
}
Rule::question => {
// Handle ternary - need to parse the rest
// This is simplified - in practice you'd need more complex handling
Ok(Expr::Ternary(Box::new(lhs), Box::new(rhs), Box::new(Expr::None)))
}
_ => Err(format!("Unexpected infix operator: {:?}", op.as_rule()))
}
}
fn parse_prefix(&self, op: Pair<Rule>, rhs: Result<Expr, String>) -> Result<Expr, String> {
let rhs = rhs?;
match op.as_rule() {
Rule::not => Ok(Expr::Unary(UnaryOp::Not, Box::new(rhs))),
Rule::minus => Ok(Expr::Unary(UnaryOp::Neg, Box::new(rhs))),
Rule::increment => Ok(Expr::Unary(UnaryOp::PreIncrement, Box::new(rhs))),
Rule::decrement => Ok(Expr::Unary(UnaryOp::PreDecrement, Box::new(rhs))),
_ => Err(format!("Unexpected prefix operator: {:?}", op.as_rule()))
}
}
fn parse_postfix(&self, lhs: Result<Expr, String>, op: Pair<Rule>) -> Result<Expr, String> {
let lhs = lhs?;
match op.as_rule() {
Rule::increment => Ok(Expr::PostIncrement(Box::new(lhs))),
Rule::decrement => Ok(Expr::PostDecrement(Box::new(lhs))),
_ => Err(format!("Unexpected postfix operator: {:?}", op.as_rule()))
}
}
fn parse_if_expr(&self, pair: Pair<Rule>) -> Result<Expr, String> {
let mut inner = pair.into_inner();
let condition = self.parse_expr(inner.next().unwrap().into_inner())?;
let then_block = self.parse_block(inner.next().unwrap())?;
let else_block = inner.next().map(|p| self.parse_block(p)).transpose()?;
Ok(Expr::If(Box::new(condition), Box::new(then_block), else_block.map(Box::new)))
}
fn parse_match_expr(&self, pair: Pair<Rule>) -> Result<Expr, String> {
let mut inner = pair.into_inner();
let target = if let Some(p) = inner.peek() {
if p.as_rule() == Rule::expression {
Some(Box::new(self.parse_expr(inner.next().unwrap().into_inner())?))
} else {
None
}
} else {
None
};
let mut arms = vec![];
for arm_pair in inner {
if arm_pair.as_rule() == Rule::match_arm {
arms.push(self.parse_match_arm(arm_pair)?);
}
}
Ok(Expr::Match(target, arms))
}
fn parse_match_arm(&self, pair: Pair<Rule>) -> Result<MatchArm, String> {
let mut inner = pair.into_inner();
let pattern = self.parse_match_pattern(inner.next().unwrap())?;
let body = self.parse_expr(inner.next().unwrap().into_inner())?;
Ok(MatchArm { pattern, body })
}
fn parse_match_pattern(&self, pair: Pair<Rule>) -> Result<MatchPattern, String> {
// Simplified pattern parsing
match pair.as_rule() {
Rule::underscore => Ok(MatchPattern::Wildcard),
_ => Ok(MatchPattern::Expression(self.parse_expr(pair.into_inner())?))
}
}
fn parse_block(&self, pair: Pair<Rule>) -> Result<Block, String> {
let mut statements = vec![];
for stmt in pair.into_inner() {
statements.push(self.parse_statement(stmt)?);
}
Ok(Block { statements })
}
fn parse_statement(&self, pair: Pair<Rule>) -> Result<Statement, String> {
match pair.as_rule() {
Rule::expression_stmt => {
let expr = self.parse_expr(pair.into_inner())?;
Ok(Statement::Expression(expr))
}
Rule::return_stmt => {
let mut inner = pair.into_inner();
let expr = inner.next().map(|p| self.parse_expr(p.into_inner())).transpose()?;
Ok(Statement::Return(expr))
}
// Add other statement parsing here
_ => Err(format!("Unimplemented statement type: {:?}", pair.as_rule()))
}
}
fn parse_type(&self, pair: Pair<Rule>) -> Result<Type, String> {
match pair.as_rule() {
Rule::type_annotation => {
// Type annotation starts with colon, skip it
let mut inner = pair.into_inner();
inner.next(); // skip colon
self.parse_type_expr(inner.next().unwrap())
}
Rule::type_expr => {
self.parse_type_expr(pair)
}
_ => Err(format!("Expected type annotation or type expression, got {:?}", pair.as_rule()))
}
}
fn parse_type_expr(&self, pair: Pair<Rule>) -> Result<Type, String> {
let inner = pair.into_inner();
let mut current_type = None;
let mut is_optional = false;
let mut error_type = None;
let mut array_depth = 0;
for part in inner {
match part.as_rule() {
Rule::optional_prefix => {
is_optional = true;
}
Rule::error_union_prefix => {
// Could be just "!" or "ErrorType!"
let prefix_inner = part.into_inner();
if let Some(error_name) = prefix_inner.peek() {
error_type = Some(error_name.as_str().to_string());
} else {
error_type = Some("Error".to_string()); // Default error type
}
}
Rule::base_type => {
current_type = Some(self.parse_base_type(part)?);
}
Rule::array_suffix => {
array_depth += 1;
}
_ => {}
}
}
let mut result_type = current_type.ok_or("No base type found")?;
// Apply array suffixes
for _ in 0..array_depth {
result_type = Type::Array(Box::new(result_type));
}
// Apply error union
if let Some(err_type) = error_type {
result_type = Type::ErrorUnion(Some(err_type), Box::new(result_type));
}
// Apply optional
if is_optional {
result_type = Type::Optional(Box::new(result_type));
}
Ok(result_type)
}
fn parse_base_type(&self, pair: Pair<Rule>) -> Result<Type, String> {
let mut inner = pair.into_inner();
let type_part = inner.next().unwrap();
match type_part.as_rule() {
Rule::primitive_type => {
Ok(Type::Primitive(type_part.as_str().to_string()))
}
Rule::identifier => {
Ok(Type::Named(type_part.as_str().to_string()))
}
_ => Err(format!("Unexpected base type: {:?}", type_part.as_rule()))
}
}
fn parse_param_list(&self, pair: Pair<Rule>) -> Result<Vec<Param>, String> {
let mut params = vec![];
for inner in pair.into_inner() {
if inner.as_rule() == Rule::param {
params.push(self.parse_param(inner)?);
}
}
Ok(params)
}
fn parse_param(&self, pair: Pair<Rule>) -> Result<Param, String> {
let mut inner = pair.into_inner();
let name = inner.next().unwrap().as_str().to_string();
let type_annotation = if let Some(type_pair) = inner.next() {
Some(self.parse_type(type_pair)?)
} else {
None
};
Ok(Param { name, type_annotation })
}
fn parse_return_type(&self, pair: Pair<Rule>) -> Result<ReturnType, String> {
let mut inner = pair.into_inner();
let first = inner.next().unwrap();
match first.as_rule() {
Rule::arrow_fat => {
// Named return variable: => (name: Type = default?)
let name = inner.next().unwrap().as_str().to_string();
let type_annotation = self.parse_type(inner.next().unwrap())?;
let default_value = if let Some(expr_pair) = inner.next() {
Some(self.parse_expr(expr_pair.into_inner())?)
} else {
None
};
Ok(ReturnType::Named {
name,
type_annotation,
default_value,
})
}
Rule::type_annotation => {
// Simple return type: : Type
Ok(ReturnType::Simple(self.parse_type(first)?))
}
_ => Err(format!("Unexpected return type: {:?}", first.as_rule()))
}
}
fn parse_function_decl(&self, pair: Pair<Rule>) -> Result<Statement, String> {
let mut inner = pair.into_inner();
// Skip 'fn' keyword
inner.next();
let name = inner.next().unwrap().as_str().to_string();
let params = self.parse_param_list(inner.next().unwrap())?;
let mut extends = None;
let mut return_type = None;
let mut body = None;
while let Some(remaining) = inner.next() {
match remaining.as_rule() {
Rule::extends_kw => {
extends = Some(inner.next().unwrap().as_str().to_string());
}
Rule::return_type => {
return_type = Some(self.parse_return_type(remaining)?);
}
Rule::block => {
body = Some(self.parse_block(remaining)?);
}
_ => {}
}
}
Ok(Statement::Function {
name,
params,
return_type,
extends,
body: body.ok_or("Function body required")?,
})
}
}
/* Usage example
pub fn parse_solace_code(input: &str) -> Result<Vec<Statement>, String> {
let pairs = SolaceParser::parse(Rule::program, input)
.map_err(|e| format!("Parse error: {}", e))?;
let parser = SolacePrattParser::new();
let mut statements = vec![];
for pair in pairs {
if pair.as_rule() == Rule::program {
for stmt_pair in pair.into_inner() {
if stmt_pair.as_rule() != Rule::EOI {
statements.push(parser.parse_statement(stmt_pair)?);
}
}
}
}
Ok(statements)
}
*/
fn main() {
let unparsed_file = fs::read_to_string("test.solace").expect("Cannot read test file");
match SolaceParser::parse(Rule::program, &unparsed_file) {
Ok(mut pairs) => {
let program = pairs.next().unwrap();
println!("Parsing was successful.");
print_parse_tree(program, 0);
}
Err(err) => {
println!("Parse error: {}", err);
}
}
}
fn print_parse_tree(pair: Pair<Rule>, indent: usize) {
let indent_str = " ".repeat(indent);
println!("{}{:?}: \"{}\"", indent_str, pair.as_rule(), pair.as_str());
for inner_pair in pair.into_inner() {
print_parse_tree(inner_pair, indent + 1)
}
}

612
src/parser/ast.rs Normal file
View file

@ -0,0 +1,612 @@
use super::{ParseError, Rule};
use pest::iterators::{Pair, Pairs};
use pest::pratt_parser::{Assoc, Op, PrattParser};
#[derive(Debug, Clone)]
pub enum Expr {
//Empty
Empty,
// Literals
Number(f64),
String(String),
Boolean(bool),
None,
Undefined,
Underscore,
// Variables and calls
Identifier(String),
MemberAccess(Box<Expr>, String),
Index(Box<Expr>, Box<Expr>),
Call(Box<Expr>, Vec<Expr>),
// Operators
Binary(BinaryOp, Box<Expr>, Box<Expr>),
Unary(UnaryOp, Box<Expr>),
Ternary(Box<Expr>, Box<Expr>, Box<Expr>),
Assignment(String, Box<Expr>),
// Control flow
If(Box<Expr>, Box<Block>, Option<Box<Block>>),
Match(Option<Box<Expr>>, Vec<MatchArm>),
// Collections
Array(Vec<Expr>),
// Postfix
PostIncrement(Box<Expr>),
PostDecrement(Box<Expr>),
}
#[derive(Debug, Clone)]
pub enum BinaryOp {
Add,
Sub,
Mul,
Div,
Mod,
Eq,
Ne,
Lt,
Gt,
Le,
Ge,
And,
Or,
RangeInclusive,
RangeExclusive,
Is,
}
#[derive(Debug, Clone)]
pub enum UnaryOp {
Not,
Neg,
PreIncrement,
PreDecrement,
}
#[derive(Debug, Clone)]
pub struct MatchArm {
pattern: MatchPattern,
body: Expr,
}
#[derive(Debug, Clone)]
pub enum MatchPattern {
Wildcard,
Expression(Expr),
Condition(Vec<(BinaryOp, Expr)>),
}
#[derive(Debug, Clone)]
pub struct Block {
statements: Vec<Statement>,
}
#[derive(Debug, Clone)]
pub enum ReturnType {
Simple(Type),
Named {
name: String,
type_annotation: Type,
default_value: Option<Expr>,
},
}
#[derive(Debug, Clone)]
pub enum Statement {
Import {
name: String,
from: String,
},
Function {
name: String,
params: Vec<Param>,
return_type: Option<ReturnType>,
extends: Option<String>,
body: Block,
},
Variable {
kind: VarKind,
name: String,
type_annotation: Option<Type>,
value: Expr,
},
Defer {
condition: Option<Expr>,
binding: Option<String>,
body: Block,
},
Watch {
target: String,
body: Block,
},
Return(Option<Expr>),
If(Expr, Box<Statement>, Option<Box<Statement>>),
For {
var: String,
index: Option<String>,
iterable: Expr,
body: Box<Statement>,
},
While(Expr, Box<Statement>),
Expression(Expr),
Block(Block),
}
#[derive(Debug, Clone)]
pub enum VarKind {
Const,
Var,
Live,
}
#[derive(Debug, Clone)]
pub struct Param {
name: String,
type_annotation: Option<Type>,
}
#[derive(Debug, Clone)]
pub enum Type {
Primitive(String),
Array(Box<Type>),
Optional(Box<Type>),
ErrorUnion(Option<String>, Box<Type>),
Named(String),
}
pub struct SolacePrattParser {
pratt: PrattParser<Rule>,
}
impl SolacePrattParser {
pub fn new() -> Self {
let pratt = PrattParser::new()
.op(Op::infix(Rule::assign, Assoc::Right))
.op(Op::infix(Rule::question, Assoc::Right) | Op::infix(Rule::colon, Assoc::Right))
.op(Op::infix(Rule::or, Assoc::Left))
.op(Op::infix(Rule::and, Assoc::Left))
.op(Op::infix(Rule::eq, Assoc::Left)
| Op::infix(Rule::ne, Assoc::Left)
| Op::infix(Rule::is_kw, Assoc::Left))
.op(Op::infix(Rule::lt, Assoc::Left)
| Op::infix(Rule::gt, Assoc::Left)
| Op::infix(Rule::le, Assoc::Left)
| Op::infix(Rule::ge, Assoc::Left))
.op(Op::infix(Rule::range_inclusive, Assoc::Left)
| Op::infix(Rule::range_exclusive, Assoc::Left))
.op(Op::infix(Rule::plus, Assoc::Left) | Op::infix(Rule::minus, Assoc::Left))
.op(Op::infix(Rule::multiply, Assoc::Left)
| Op::infix(Rule::divide, Assoc::Left)
| Op::infix(Rule::modulo, Assoc::Left))
.op(Op::prefix(Rule::not)
| Op::prefix(Rule::minus)
| Op::prefix(Rule::increment)
| Op::prefix(Rule::decrement))
.op(Op::postfix(Rule::increment) | Op::postfix(Rule::decrement));
SolacePrattParser { pratt }
}
pub fn parse_expr(&self, pairs: Pairs<Rule>) -> Result<Expr, ParseError> {
if pairs.clone().count() == 0 {
return Ok(Expr::Empty);
}
self.pratt
.map_primary(|primary| self.parse_primary(primary))
.map_infix(|lhs, op, rhs| self.parse_infix(lhs, op, rhs))
.map_prefix(|op, rhs| self.parse_prefix(op, rhs))
.map_postfix(|lhs, op| self.parse_postfix(lhs, op))
.parse(pairs)
}
fn parse_primary(&self, pair: Pair<Rule>) -> Result<Expr, ParseError> {
match pair.as_rule() {
Rule::number_literal => {
let num = pair
.as_str()
.parse::<f64>()
.map_err(|_| ParseError::InvalidNumber(pair.as_rule()))?;
Ok(Expr::Number(num))
}
Rule::string_literal => {
let s = pair.as_str();
Ok(Expr::String(s[1..s.len() - 1].to_string()))
}
Rule::boolean_literal => Ok(Expr::Boolean(pair.as_str() == "true")),
Rule::none_kw => Ok(Expr::None),
Rule::undefined_kw => Ok(Expr::Undefined),
Rule::underscore => Ok(Expr::Underscore),
Rule::identifier => Ok(Expr::Identifier(pair.as_str().to_string())),
Rule::array_literal => {
let mut elements = vec![];
for inner in pair.into_inner() {
if inner.as_rule() == Rule::expression {
elements.push(self.parse_expr(inner.into_inner())?);
}
}
Ok(Expr::Array(elements))
}
Rule::if_expr => self.parse_if_expr(pair),
Rule::match_expr => self.parse_match_expr(pair),
_ => Err(ParseError::UnknownPrimary(pair.as_rule())),
}
}
fn parse_infix(
&self,
lhs: Result<Expr, ParseError>,
op: Pair<Rule>,
rhs: Result<Expr, ParseError>,
) -> Result<Expr, ParseError> {
let lhs = lhs?;
let rhs = rhs?;
match op.as_rule() {
Rule::plus => Ok(Expr::Binary(BinaryOp::Add, Box::new(lhs), Box::new(rhs))),
Rule::minus => Ok(Expr::Binary(BinaryOp::Sub, Box::new(lhs), Box::new(rhs))),
Rule::multiply => Ok(Expr::Binary(BinaryOp::Mul, Box::new(lhs), Box::new(rhs))),
Rule::divide => Ok(Expr::Binary(BinaryOp::Div, Box::new(lhs), Box::new(rhs))),
Rule::modulo => Ok(Expr::Binary(BinaryOp::Mod, Box::new(lhs), Box::new(rhs))),
Rule::eq => Ok(Expr::Binary(BinaryOp::Eq, Box::new(lhs), Box::new(rhs))),
Rule::ne => Ok(Expr::Binary(BinaryOp::Ne, Box::new(lhs), Box::new(rhs))),
Rule::lt => Ok(Expr::Binary(BinaryOp::Lt, Box::new(lhs), Box::new(rhs))),
Rule::gt => Ok(Expr::Binary(BinaryOp::Gt, Box::new(lhs), Box::new(rhs))),
Rule::le => Ok(Expr::Binary(BinaryOp::Le, Box::new(lhs), Box::new(rhs))),
Rule::ge => Ok(Expr::Binary(BinaryOp::Ge, Box::new(lhs), Box::new(rhs))),
Rule::and => Ok(Expr::Binary(BinaryOp::And, Box::new(lhs), Box::new(rhs))),
Rule::or => Ok(Expr::Binary(BinaryOp::Or, Box::new(lhs), Box::new(rhs))),
Rule::is_kw => Ok(Expr::Binary(BinaryOp::Is, Box::new(lhs), Box::new(rhs))),
Rule::range_inclusive => Ok(Expr::Binary(
BinaryOp::RangeInclusive,
Box::new(lhs),
Box::new(rhs),
)),
Rule::range_exclusive => Ok(Expr::Binary(
BinaryOp::RangeExclusive,
Box::new(lhs),
Box::new(rhs),
)),
Rule::assign => {
if let Expr::Identifier(name) = lhs {
Ok(Expr::Assignment(name, Box::new(rhs)))
} else {
Err(ParseError::InvalidLeftHand(lhs))
}
}
Rule::question => {
// Handle ternary - need to parse the rest
// This is simplified - in practice you'd need more complex handling
Ok(Expr::Ternary(
Box::new(lhs),
Box::new(rhs),
Box::new(Expr::None),
))
}
_ => Err(ParseError::UnknownInfixOperator(op.as_rule())),
}
}
fn parse_prefix(
&self,
op: Pair<Rule>,
rhs: Result<Expr, ParseError>,
) -> Result<Expr, ParseError> {
let rhs = rhs?;
match op.as_rule() {
Rule::not => Ok(Expr::Unary(UnaryOp::Not, Box::new(rhs))),
Rule::minus => Ok(Expr::Unary(UnaryOp::Neg, Box::new(rhs))),
Rule::increment => Ok(Expr::Unary(UnaryOp::PreIncrement, Box::new(rhs))),
Rule::decrement => Ok(Expr::Unary(UnaryOp::PreDecrement, Box::new(rhs))),
_ => Err(ParseError::UnknownPrefixOperator(op.as_rule())),
}
}
fn parse_postfix(
&self,
lhs: Result<Expr, ParseError>,
op: Pair<Rule>,
) -> Result<Expr, ParseError> {
let lhs = lhs?;
match op.as_rule() {
Rule::increment => Ok(Expr::PostIncrement(Box::new(lhs))),
Rule::decrement => Ok(Expr::PostDecrement(Box::new(lhs))),
_ => Err(ParseError::UnknownPostfixOperator(op.as_rule())),
}
}
fn parse_if_expr(&self, pair: Pair<Rule>) -> Result<Expr, ParseError> {
let mut inner = pair.into_inner();
let condition = self.parse_expr(inner.next().unwrap().into_inner())?;
let then_block = self.parse_block(inner.next().unwrap())?;
let else_block = inner.next().map(|p| self.parse_block(p)).transpose()?;
Ok(Expr::If(
Box::new(condition),
Box::new(then_block),
else_block.map(Box::new),
))
}
fn parse_match_expr(&self, pair: Pair<Rule>) -> Result<Expr, ParseError> {
let mut inner = pair.into_inner();
let target = if let Some(p) = inner.peek() {
if p.as_rule() == Rule::expression {
Some(Box::new(
self.parse_expr(inner.next().unwrap().into_inner())?,
))
} else {
None
}
} else {
None
};
let mut arms = vec![];
for arm_pair in inner {
if arm_pair.as_rule() == Rule::match_arm {
arms.push(self.parse_match_arm(arm_pair)?);
}
}
Ok(Expr::Match(target, arms))
}
fn parse_match_arm(&self, pair: Pair<Rule>) -> Result<MatchArm, ParseError> {
let mut inner = pair.into_inner();
let pattern = self.parse_match_pattern(inner.next().unwrap())?;
let body = self.parse_expr(inner.next().unwrap().into_inner())?;
Ok(MatchArm { pattern, body })
}
fn parse_match_pattern(&self, pair: Pair<Rule>) -> Result<MatchPattern, ParseError> {
// Simplified pattern parsing
match pair.as_rule() {
Rule::underscore => Ok(MatchPattern::Wildcard),
_ => Ok(MatchPattern::Expression(
self.parse_expr(pair.into_inner())?,
)),
}
}
fn parse_block(&self, pair: Pair<Rule>) -> Result<Block, ParseError> {
let mut statements = vec![];
for stmt in pair.into_inner() {
statements.push(self.parse_statement(stmt)?);
}
Ok(Block { statements })
}
fn parse_statement(&self, pair: Pair<Rule>) -> Result<Statement, ParseError> {
match pair.as_rule() {
Rule::expression_stmt => {
let expr = self.parse_expr(pair.into_inner())?;
Ok(Statement::Expression(expr))
}
Rule::return_stmt => {
let mut inner = pair.into_inner();
let expr = inner
.next()
.map(|p| self.parse_expr(p.into_inner()))
.transpose()?;
Ok(Statement::Return(expr))
}
Rule::function_decl => {
let decl = self.parse_function_decl(pair.into_inner())?;
Ok(decl)
}
// Add other statement parsing here
_ => Err(ParseError::UnknownStatement(pair.as_rule())),
}
}
fn parse_type(&self, pair: Pair<Rule>) -> Result<Type, ParseError> {
match pair.as_rule() {
Rule::type_annotation => {
// Type annotation starts with colon, skip it
let mut inner = pair.into_inner();
inner.next(); // skip colon
self.parse_type_expr(inner.next().unwrap())
}
Rule::type_expr => self.parse_type_expr(pair),
_ => Err(ParseError::ShouldBeType(pair.as_rule())),
}
}
fn parse_type_expr(&self, pair: Pair<Rule>) -> Result<Type, ParseError> {
let inner = pair.into_inner();
let mut current_type = None;
let mut is_optional = false;
let mut error_type = None;
let mut array_depth = 0;
for part in inner {
match part.as_rule() {
Rule::optional_prefix => {
is_optional = true;
}
Rule::error_union_prefix => {
// Could be just "!", which defaults to Error!, or "ErrorType!"
let prefix_inner = part.into_inner();
if let Some(error_name) = prefix_inner.peek() {
error_type = Some(error_name.as_str().to_string());
} else {
error_type = Some("Error".to_string());
}
}
Rule::base_type => {
current_type = Some(self.parse_base_type(part)?);
}
Rule::array_suffix => {
array_depth += 1;
}
_ => {}
}
}
let mut result_type = current_type.ok_or(ParseError::MissingBaseType())?;
// Apply array suffixes
for _ in 0..array_depth {
result_type = Type::Array(Box::new(result_type));
}
// Apply error union
if let Some(err_type) = error_type {
result_type = Type::ErrorUnion(Some(err_type), Box::new(result_type));
}
// Apply optional
if is_optional {
result_type = Type::Optional(Box::new(result_type));
}
Ok(result_type)
}
fn parse_base_type(&self, pair: Pair<Rule>) -> Result<Type, ParseError> {
let mut inner = pair.into_inner();
let type_part = inner.next().unwrap();
match type_part.as_rule() {
Rule::primitive_type => Ok(Type::Primitive(type_part.as_str().to_string())),
Rule::identifier => Ok(Type::Named(type_part.as_str().to_string())),
_ => Err(ParseError::UnknownTypePart(type_part.as_rule())),
}
}
fn parse_param_list(&self, pair: Pair<Rule>) -> Result<Vec<Param>, ParseError> {
let mut params = vec![];
for inner in pair.into_inner() {
if inner.as_rule() == Rule::param {
params.push(self.parse_param(inner)?);
}
}
Ok(params)
}
fn parse_param(&self, pair: Pair<Rule>) -> Result<Param, ParseError> {
let mut inner = pair.into_inner();
let name = inner.next().unwrap().as_str().to_string();
let type_annotation = if let Some(type_pair) = inner.next() {
Some(self.parse_type(type_pair)?)
} else {
None
};
Ok(Param {
name,
type_annotation,
})
}
fn parse_return_type(&self, pair: Pair<Rule>) -> Result<ReturnType, ParseError> {
let mut inner = pair.into_inner();
let wrapped = inner.next().unwrap();
// Skip intermediate rules
let first = match wrapped.as_rule() {
Rule::return_type_simple => wrapped.into_inner().next().unwrap(),
Rule::return_type_named => wrapped.into_inner().next().unwrap(),
_ => wrapped,
};
match first.as_rule() {
// Named return variable: fn foo() => (name: Type = default?) {/*...*/}
Rule::arrow_fat => {
let name = inner.next().unwrap().as_str().to_string();
let type_annotation = self.parse_type(inner.next().unwrap())?;
let default_value = if let Some(expr_pair) = inner.next() {
Some(self.parse_expr(expr_pair.into_inner())?)
} else {
None
};
Ok(ReturnType::Named {
name,
type_annotation,
default_value,
})
}
// Simple return type: fn foo(): Type {/*...*/}
Rule::type_annotation => {
let type_annotation = self.parse_type(first)?;
Ok(ReturnType::Simple(type_annotation))
}
// Brother ewww... whats that?
_ => Err(ParseError::UnknownReturnType(
first.as_str().to_owned(),
first.as_rule(),
)),
}
}
fn parse_function_decl(&self, mut pairs: Pairs<Rule>) -> Result<Statement, ParseError> {
// Skip 'fn' keyword
pairs.next();
let name = pairs.next().unwrap().as_str();
let params = self.parse_param_list(pairs.next().unwrap())?;
let mut extends = None;
let mut return_type = None;
let mut body = None;
while let Some(remaining) = pairs.next() {
match remaining.as_rule() {
Rule::extends_kw => {
extends = Some(pairs.next().unwrap().as_str().to_string());
}
Rule::return_type => {
return_type = Some(self.parse_return_type(remaining)?);
}
Rule::block => {
body = Some(self.parse_block(remaining)?);
}
_ => {}
}
}
Ok(Statement::Function {
name: name.to_owned(),
params,
return_type,
extends,
body: body.ok_or(ParseError::MissingFunctionBody(name.to_owned()))?,
})
}
}
#[derive(Debug, Clone)]
pub struct Program {
pub statements: Vec<Statement>,
}
impl Program {
pub fn from_pairs(pairs: Pairs<Rule>) -> Result<Self, ParseError> {
let parser = SolacePrattParser::new();
let mut statements = Vec::new();
for pair in pairs {
if pair.as_rule() == Rule::program {
for stmt_pair in pair.into_inner() {
if stmt_pair.as_rule() != Rule::EOI {
let stmt = parser.parse_statement(stmt_pair)?;
statements.push(stmt);
}
}
}
}
Ok(Program { statements })
}
}

76
src/parser/mod.rs Normal file
View file

@ -0,0 +1,76 @@
pub mod ast;
use pest::Parser;
use pest::iterators::Pair;
use pest_derive::Parser;
use thiserror::Error;
#[derive(Parser)]
#[grammar = "solace.pest"]
pub struct SolaceParser;
#[derive(Error, Debug)]
pub enum ParseError {
#[error("Parse error: {0}")]
PestError(#[from] pest::error::Error<Rule>),
#[error("Unexpected rule: {0:?}")]
UnexpectedRule(Rule),
#[error("Statement not implemented: {0:?}")]
UnknownStatement(Rule),
#[error("Unknown keyword, literal or expression: {0:?}")]
UnknownPrimary(Rule),
#[error("Left side of assignment must be an identifier: {0:?}")]
InvalidLeftHand(ast::Expr),
#[error("Unexpected infix operator: {0:?}")]
UnknownInfixOperator(Rule),
#[error("Unexpected prefix operator: {0:?}")]
UnknownPrefixOperator(Rule),
#[error("Unexpected postfix operator: {0:?}")]
UnknownPostfixOperator(Rule),
#[error("Expected type annotation or type expression, got: {0:?}")]
ShouldBeType(Rule),
#[error("Unexpected return type: \"{0}\" ({1:?})")]
UnknownReturnType(String, Rule),
#[error("Unexpected base type: {0:?}")]
UnknownTypePart(Rule),
#[error("Invalid Number: {0:?}")]
InvalidNumber(Rule),
#[error("No base type found")]
MissingBaseType(),
#[error("Function body required: {0}")]
MissingFunctionBody(String),
}
pub fn parse(input: &str, debug: bool) -> Result<ast::Program, ParseError> {
if debug {
let mut debug_pairs = SolaceParser::parse(Rule::program, input)?;
print_parse_tree(debug_pairs.next().unwrap(), 0);
}
let pairs = SolaceParser::parse(Rule::program, input)?;
let program = ast::Program::from_pairs(pairs)?;
Ok(program)
}
fn print_parse_tree(pair: Pair<Rule>, indent: usize) {
let indent_str = " ".repeat(indent);
println!("{}{:?}: \"{}\"", indent_str, pair.as_rule(), pair.as_str());
for inner_pair in pair.into_inner() {
print_parse_tree(inner_pair, indent + 1)
}
}

View file

@ -86,20 +86,12 @@ comparison_op = { le | ge | eq | ne | lt | gt }
increment = @{ "++" }
decrement = @{ "--" }
// Delimiters
lparen = { "(" }
rparen = { ")" }
lbrace = { "{" }
rbrace = { "}" }
lbracket = { "[" }
rbracket = { "]" }
// Types - Fixed to avoid left recursion
primitive_type = { "string" | "number" | "boolean" | "undefined" }
base_type = { primitive_type | identifier }
// Type suffixes
array_suffix = { lbracket ~ rbracket }
array_suffix = { "[" ~ "]" }
optional_prefix = { question }
error_union_prefix = { identifier? ~ not }
@ -115,11 +107,11 @@ type_annotation = { colon ~ type_expr }
// Parameters
optional_suffix = { question }
param = { identifier ~ optional_suffix? ~ type_annotation? }
param_list = { lparen ~ (param ~ (comma ~ param)*)? ~ rparen }
param_list = { "(" ~ (param ~ (comma ~ param)*)? ~ ")" }
// Return type with named return variable
return_type_simple = { type_annotation }
return_type_named = { arrow_fat ~ lparen ~ identifier ~ type_annotation ~ (assign ~ expression)? ~ rparen }
return_type_named = { arrow_fat ~ "(" ~ identifier ~ type_annotation ~ (assign ~ expression)? ~ ")" }
return_type = { return_type_simple | return_type_named }
// Expressions
@ -133,23 +125,25 @@ primary_expr = {
undefined_kw |
function_call_expr |
identifier |
lparen ~ expression ~ rparen |
grouped_expression |
array_literal |
if_expr |
match_expr |
continue_expr
}
// Function calls including ok() and err()
function_call_expr = { (ok_kw | err_kw | failure_kw | success_kw) ~ lparen ~ expression_list? ~ rparen }
grouped_expression = { "(" ~ expression ~ ")" }
array_literal = { lbracket ~ expression_list? ~ rbracket }
// Function calls including ok() and err()
function_call_expr = { (ok_kw | err_kw | failure_kw | success_kw) ~ "(" ~ expression_list? ~ ")" }
array_literal = { "[" ~ expression_list? ~ "]" }
expression_list = { expression ~ (comma ~ expression)* }
// Member access and indexing
member_access = { dot ~ identifier }
index_access = { lbracket ~ expression ~ rbracket }
call_suffix = { lparen ~ expression_list? ~ rparen }
index_access = { "[" ~ expression ~ "]" }
call_suffix = { "(" ~ expression_list? ~ ")" }
postfix_expr = { primary_expr ~ (member_access | index_access | call_suffix | increment | decrement)* }
@ -164,16 +158,16 @@ logical_and_expr = { equality_expr ~ (and ~ equality_expr)* }
logical_or_expr = { logical_and_expr ~ (or ~ logical_and_expr)* }
ternary_expr = { logical_or_expr ~ (question ~ expression ~ colon ~ expression)? }
assignment_expr = { lvalue ~ assign ~ expression }
lvalue = { identifier ~ (dot ~ identifier | lbracket ~ expression ~ rbracket)* }
lvalue = { identifier ~ (dot ~ identifier | "[" ~ expression ~ "]")* }
expression = { assignment_expr | ternary_expr }
// If expression and statement
if_expr = { if_kw ~ expression ~ lbrace ~ statement* ~ rbrace ~ (else_kw ~ lbrace ~ statement* ~ rbrace)? }
if_expr = { if_kw ~ expression ~ "{" ~ statement* ~ "}" ~ (else_kw ~ "{" ~ statement* ~ "}")? }
if_expr_short = { if_kw ~ expression ~ colon ~ expression ~ (else_kw ~ colon ~ expression)? }
// Match expression
match_expr = { match_kw ~ expression? ~ lbrace ~ match_arm* ~ rbrace }
match_expr = { match_kw ~ expression? ~ "{" ~ match_arm* ~ "}" }
match_arm = { match_pattern ~ arrow ~ (expression | block) }
match_pattern = { expression }
@ -181,7 +175,7 @@ match_pattern = { expression }
continue_expr = { continue_kw }
// Statements
statement = {
statement = _{
import_stmt |
function_decl |
variable_decl |
@ -230,12 +224,12 @@ while_stmt = {
continue_stmt = { continue_kw }
match_stmt = { match_kw ~ expression? ~ lbrace ~ match_arm* ~ rbrace }
match_stmt = { match_kw ~ expression? ~ "{" ~ match_arm* ~ "}" }
expression_stmt = { expression }
// Blocks
block = { lbrace ~ statement* ~ rbrace }
block = { "{" ~ statement* ~ "}" }
// Program
program = { SOI ~ statement* ~ EOI }

14
src/transformer/js.rs Normal file
View file

@ -0,0 +1,14 @@
use crate::parser::ast;
use swc_ecma_ast as swc_ast;
use swc_common::DUMMY_SP;
pub struct JsTransformer;
impl JsTransformer {
pub fn new() -> Self {
Self
}
pub fn transform(&self, program: ast::Program) -> swc_ast::Module {
todo!("Implement Solace AST to SWC AST transformer")
}
}

4
src/transformer/mod.rs Normal file
View file

@ -0,0 +1,4 @@
pub mod js;
// maybe one day:
// pub mod wasm;

3
test.short.solace Normal file
View file

@ -0,0 +1,3 @@
fn timesTwo(x: number): number {
return x*2
}