some more structure and grammar parser fixes
This commit is contained in:
parent
dd42ef23d4
commit
12c0431e69
11 changed files with 1919 additions and 579 deletions
1117
Cargo.lock
generated
1117
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -4,6 +4,11 @@ version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
anyhow = "1.0.98"
|
||||||
lazy_static = "1.5.0"
|
lazy_static = "1.5.0"
|
||||||
pest = "2.8.1"
|
pest = "2.8.1"
|
||||||
pest_derive = "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
35
src/emitter/js.rs
Normal 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
1
src/emitter/mod.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pub mod js;
|
591
src/main.rs
591
src/main.rs
|
@ -1,561 +1,40 @@
|
||||||
use std::fs;
|
mod emitter;
|
||||||
use pest::iterators::{Pair, Pairs};
|
mod parser;
|
||||||
use pest::pratt_parser::{Assoc, Op, PrattParser};
|
mod transformer;
|
||||||
use pest::Parser;
|
|
||||||
use pest_derive::Parser;
|
|
||||||
|
|
||||||
#[derive(Parser)]
|
use anyhow::Result;
|
||||||
#[grammar = "solace.pest"]
|
use std::{env, fs};
|
||||||
pub struct SolaceParser;
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Error, Debug)]
|
||||||
pub enum Expr {
|
pub enum ArgumentError {
|
||||||
// Literals
|
#[error("Usage: {0} ./path/to/file.solace")]
|
||||||
Number(f64),
|
MissingSourceFile(String),
|
||||||
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)]
|
fn main() -> Result<()> {
|
||||||
pub enum BinaryOp {
|
let args: Vec<String> = env::args().collect();
|
||||||
Add, Sub, Mul, Div, Mod,
|
|
||||||
Eq, Ne, Lt, Gt, Le, Ge,
|
if args.len() < 2 {
|
||||||
And, Or,
|
return Err(ArgumentError::MissingSourceFile(args[0].clone()).into());
|
||||||
RangeInclusive, RangeExclusive,
|
}
|
||||||
Is,
|
|
||||||
|
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
612
src/parser/ast.rs
Normal 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
76
src/parser/mod.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -86,20 +86,12 @@ comparison_op = { le | ge | eq | ne | lt | gt }
|
||||||
increment = @{ "++" }
|
increment = @{ "++" }
|
||||||
decrement = @{ "--" }
|
decrement = @{ "--" }
|
||||||
|
|
||||||
// Delimiters
|
|
||||||
lparen = { "(" }
|
|
||||||
rparen = { ")" }
|
|
||||||
lbrace = { "{" }
|
|
||||||
rbrace = { "}" }
|
|
||||||
lbracket = { "[" }
|
|
||||||
rbracket = { "]" }
|
|
||||||
|
|
||||||
// Types - Fixed to avoid left recursion
|
// Types - Fixed to avoid left recursion
|
||||||
primitive_type = { "string" | "number" | "boolean" | "undefined" }
|
primitive_type = { "string" | "number" | "boolean" | "undefined" }
|
||||||
base_type = { primitive_type | identifier }
|
base_type = { primitive_type | identifier }
|
||||||
|
|
||||||
// Type suffixes
|
// Type suffixes
|
||||||
array_suffix = { lbracket ~ rbracket }
|
array_suffix = { "[" ~ "]" }
|
||||||
optional_prefix = { question }
|
optional_prefix = { question }
|
||||||
error_union_prefix = { identifier? ~ not }
|
error_union_prefix = { identifier? ~ not }
|
||||||
|
|
||||||
|
@ -115,11 +107,11 @@ type_annotation = { colon ~ type_expr }
|
||||||
// Parameters
|
// Parameters
|
||||||
optional_suffix = { question }
|
optional_suffix = { question }
|
||||||
param = { identifier ~ optional_suffix? ~ type_annotation? }
|
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 with named return variable
|
||||||
return_type_simple = { type_annotation }
|
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 }
|
return_type = { return_type_simple | return_type_named }
|
||||||
|
|
||||||
// Expressions
|
// Expressions
|
||||||
|
@ -133,23 +125,25 @@ primary_expr = {
|
||||||
undefined_kw |
|
undefined_kw |
|
||||||
function_call_expr |
|
function_call_expr |
|
||||||
identifier |
|
identifier |
|
||||||
lparen ~ expression ~ rparen |
|
grouped_expression |
|
||||||
array_literal |
|
array_literal |
|
||||||
if_expr |
|
if_expr |
|
||||||
match_expr |
|
match_expr |
|
||||||
continue_expr
|
continue_expr
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function calls including ok() and err()
|
grouped_expression = { "(" ~ expression ~ ")" }
|
||||||
function_call_expr = { (ok_kw | err_kw | failure_kw | success_kw) ~ lparen ~ expression_list? ~ rparen }
|
|
||||||
|
|
||||||
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)* }
|
expression_list = { expression ~ (comma ~ expression)* }
|
||||||
|
|
||||||
// Member access and indexing
|
// Member access and indexing
|
||||||
member_access = { dot ~ identifier }
|
member_access = { dot ~ identifier }
|
||||||
index_access = { lbracket ~ expression ~ rbracket }
|
index_access = { "[" ~ expression ~ "]" }
|
||||||
call_suffix = { lparen ~ expression_list? ~ rparen }
|
call_suffix = { "(" ~ expression_list? ~ ")" }
|
||||||
|
|
||||||
postfix_expr = { primary_expr ~ (member_access | index_access | call_suffix | increment | decrement)* }
|
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)* }
|
logical_or_expr = { logical_and_expr ~ (or ~ logical_and_expr)* }
|
||||||
ternary_expr = { logical_or_expr ~ (question ~ expression ~ colon ~ expression)? }
|
ternary_expr = { logical_or_expr ~ (question ~ expression ~ colon ~ expression)? }
|
||||||
assignment_expr = { lvalue ~ assign ~ expression }
|
assignment_expr = { lvalue ~ assign ~ expression }
|
||||||
lvalue = { identifier ~ (dot ~ identifier | lbracket ~ expression ~ rbracket)* }
|
lvalue = { identifier ~ (dot ~ identifier | "[" ~ expression ~ "]")* }
|
||||||
|
|
||||||
expression = { assignment_expr | ternary_expr }
|
expression = { assignment_expr | ternary_expr }
|
||||||
|
|
||||||
// If expression and statement
|
// 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)? }
|
if_expr_short = { if_kw ~ expression ~ colon ~ expression ~ (else_kw ~ colon ~ expression)? }
|
||||||
|
|
||||||
// Match 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_arm = { match_pattern ~ arrow ~ (expression | block) }
|
||||||
match_pattern = { expression }
|
match_pattern = { expression }
|
||||||
|
|
||||||
|
@ -181,7 +175,7 @@ match_pattern = { expression }
|
||||||
continue_expr = { continue_kw }
|
continue_expr = { continue_kw }
|
||||||
|
|
||||||
// Statements
|
// Statements
|
||||||
statement = {
|
statement = _{
|
||||||
import_stmt |
|
import_stmt |
|
||||||
function_decl |
|
function_decl |
|
||||||
variable_decl |
|
variable_decl |
|
||||||
|
@ -230,12 +224,12 @@ while_stmt = {
|
||||||
|
|
||||||
continue_stmt = { continue_kw }
|
continue_stmt = { continue_kw }
|
||||||
|
|
||||||
match_stmt = { match_kw ~ expression? ~ lbrace ~ match_arm* ~ rbrace }
|
match_stmt = { match_kw ~ expression? ~ "{" ~ match_arm* ~ "}" }
|
||||||
|
|
||||||
expression_stmt = { expression }
|
expression_stmt = { expression }
|
||||||
|
|
||||||
// Blocks
|
// Blocks
|
||||||
block = { lbrace ~ statement* ~ rbrace }
|
block = { "{" ~ statement* ~ "}" }
|
||||||
|
|
||||||
// Program
|
// Program
|
||||||
program = { SOI ~ statement* ~ EOI }
|
program = { SOI ~ statement* ~ EOI }
|
||||||
|
|
14
src/transformer/js.rs
Normal file
14
src/transformer/js.rs
Normal 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
4
src/transformer/mod.rs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
pub mod js;
|
||||||
|
|
||||||
|
// maybe one day:
|
||||||
|
// pub mod wasm;
|
3
test.short.solace
Normal file
3
test.short.solace
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
fn timesTwo(x: number): number {
|
||||||
|
return x*2
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue