From 4e36be3684d4642c4c1199bd8a86618c20086dc7 Mon Sep 17 00:00:00 2001 From: Benjamin Bolte Date: Wed, 30 Oct 2024 21:24:12 -0700 Subject: [PATCH 1/7] simplified klang --- examples/simple.k | 16 +- klang/build.rs | 5 +- klang/src/bin/kcompile.rs | 4 +- klang/src/lib.rs | 8 +- klang/src/parser/errors.rs | 6 +- klang/src/parser/expressions.rs | 313 -------------------------------- klang/src/parser/functions.rs | 94 ---------- klang/src/parser/lang.rs | 77 ++++++++ klang/src/parser/literals.rs | 86 --------- klang/src/parser/mod.rs | 83 ++++----- klang/src/parser/passes/mod.rs | 96 ++++++++++ klang/src/parser/statements.rs | 117 ------------ klang/src/pest/klang.pest | 109 +---------- klang/src/proto/ast.proto | 142 +-------------- klang/src/proto/ir.proto | 30 +++ 15 files changed, 276 insertions(+), 910 deletions(-) delete mode 100644 klang/src/parser/expressions.rs delete mode 100644 klang/src/parser/functions.rs create mode 100644 klang/src/parser/lang.rs delete mode 100644 klang/src/parser/literals.rs create mode 100644 klang/src/parser/passes/mod.rs delete mode 100644 klang/src/parser/statements.rs create mode 100644 klang/src/proto/ir.proto diff --git a/examples/simple.k b/examples/simple.k index 72f5ecb..95d6f7a 100644 --- a/examples/simple.k +++ b/examples/simple.k @@ -1,10 +1,12 @@ -fn wave(limb: string, joint: number, amount: number) : "Wave a hand back and forth" { - let pos = get_position(limb: limb, joint: joint); - let pos = set_position(limb: limb, joint: joint, pos: pos + amount); - let pos = set_position(limb: limb, joint: joint, pos: pos - amount); - return pos; +> wave [arm] arm { + move [arm] joint 1 to 90 + move [arm] joint 2 to 90 + move [arm] joint 3 to 90 } -fn main() : "Wave a hand back and forth a few times" { - wave(limb: "right_arm", joint: 0, amount: 45deg); +> wave both arms { + " wave [right] arm + " wave [left] arm } + +" wave both arms diff --git a/klang/build.rs b/klang/build.rs index ec2faed..2aea3ba 100644 --- a/klang/build.rs +++ b/klang/build.rs @@ -11,6 +11,9 @@ fn main() { config.enable_type_names(); config - .compile_protos(&["src/proto/ast.proto"], &["src/proto/"]) + .compile_protos( + &["src/proto/ast.proto", "src/proto/ir.proto"], + &["src/proto/"], + ) .unwrap(); } diff --git a/klang/src/bin/kcompile.rs b/klang/src/bin/kcompile.rs index ce91d3f..75a4b5b 100644 --- a/klang/src/bin/kcompile.rs +++ b/klang/src/bin/kcompile.rs @@ -5,14 +5,14 @@ use std::path::Path; // Import from the library fn main() { let args: Vec = env::args().collect(); match args.len() { - 2 => match compile_file_inplace(Path::new(&args[1])) { + 2 => match compile_file_inplace(Path::new(&args[1]), false) { Ok(_) => (), Err(e) => { eprintln!("{}", e); std::process::exit(1); } }, - 3 => match compile_file(Path::new(&args[1]), Path::new(&args[2])) { + 3 => match compile_file(Path::new(&args[1]), Path::new(&args[2]), false) { Ok(_) => (), Err(e) => { eprintln!("{}", e); diff --git a/klang/src/lib.rs b/klang/src/lib.rs index 5a227a1..08d3f9c 100644 --- a/klang/src/lib.rs +++ b/klang/src/lib.rs @@ -4,13 +4,13 @@ use crate::parser::errors::ParseError; use crate::parser::{parse_file, write_program_to_file}; use std::path::Path; -pub fn compile_file(file_path: &Path, output_path: &Path) -> Result<(), ParseError> { +pub fn compile_file(file_path: &Path, output_path: &Path, binary: bool) -> Result<(), ParseError> { match parse_file(file_path) { - Ok(program) => write_program_to_file(&program, output_path), + Ok(program) => write_program_to_file(&program, output_path, binary), Err(e) => Err(e), } } -pub fn compile_file_inplace(file_path: &Path) -> Result<(), ParseError> { - compile_file(file_path, file_path.with_extension("ko").as_path()) +pub fn compile_file_inplace(file_path: &Path, binary: bool) -> Result<(), ParseError> { + compile_file(file_path, file_path.with_extension("ko").as_path(), binary) } diff --git a/klang/src/parser/errors.rs b/klang/src/parser/errors.rs index af76f45..c524c7c 100644 --- a/klang/src/parser/errors.rs +++ b/klang/src/parser/errors.rs @@ -31,7 +31,11 @@ impl ParseError { impl fmt::Display for ParseError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.message) + write!( + f, + "{} (line: {}, column: {})", + self.message, self.line, self.column + ) } } diff --git a/klang/src/parser/expressions.rs b/klang/src/parser/expressions.rs deleted file mode 100644 index e4ffcb7..0000000 --- a/klang/src/parser/expressions.rs +++ /dev/null @@ -1,313 +0,0 @@ -use super::ast::*; -use super::errors::ParseError; -use super::literals::{parse_identifier, parse_literal}; -use super::structs::Rule; - -pub(crate) fn parse_expression( - pair: pest::iterators::Pair, -) -> Result { - match pair.as_rule() { - Rule::expression => parse_expression(pair.into_inner().next().unwrap()), - Rule::conditional => parse_conditional(pair), - Rule::logical_or => parse_logical_or(pair), - Rule::logical_and => parse_logical_and(pair), - Rule::equality => parse_equality(pair), - Rule::comparison => parse_comparison(pair), - Rule::additive => parse_additive(pair), - Rule::multiplicative => parse_multiplicative(pair), - Rule::unary => parse_unary(pair), - Rule::postfix => parse_postfix(pair), - Rule::primary => parse_primary(pair), - // _ => panic!("Unknown expression type: {:?}", pair.as_rule()), - _ => Ok(Expression { expr: None }), - } -} - -fn parse_conditional(pair: pest::iterators::Pair) -> Result { - let mut parts = pair.clone().into_inner(); - match parts.len() { - 1 => parse_expression(parts.next().unwrap()), - 3 => { - let condition = parse_expression(parts.next().unwrap())?; - let if_true = parse_expression(parts.next().unwrap())?; - let if_false = parse_expression(parts.next().unwrap())?; - - Ok(Expression { - expr: Some(expression::Expr::Conditional(Box::new(ConditionalExpr { - condition: Some(Box::new(condition)), - then_expr: Some(Box::new(if_true)), - else_expr: Some(Box::new(if_false)), - }))), - }) - } - _ => Err(ParseError::from_pair( - format!( - "Unexpected number of parts in conditional: {:?}", - parts.len() - ), - pair, - )), - } -} - -fn parse_logical_or(pair: pest::iterators::Pair) -> Result { - let mut parts = pair.clone().into_inner(); - match parts.len() { - 1 => parse_expression(parts.next().unwrap()), - 2 => { - let left = parse_expression(parts.next().unwrap())?; - let right = parse_expression(parts.next().unwrap())?; - - Ok(Expression { - expr: Some(expression::Expr::Binary(Box::new(BinaryExpr { - operator: BinaryOperator::Or.into(), - left: Some(Box::new(left)), - right: Some(Box::new(right)), - }))), - }) - } - _ => Err(ParseError::from_pair( - format!( - "Unexpected number of parts in logical or: {:?}", - parts.len() - ), - pair, - )), - } -} - -fn parse_logical_and(pair: pest::iterators::Pair) -> Result { - let mut parts = pair.clone().into_inner(); - match parts.len() { - 1 => parse_expression(parts.next().unwrap()), - 2 => { - let left = parse_expression(parts.next().unwrap())?; - let right = parse_expression(parts.next().unwrap())?; - - Ok(Expression { - expr: Some(expression::Expr::Binary(Box::new(BinaryExpr { - operator: BinaryOperator::And.into(), - left: Some(Box::new(left)), - right: Some(Box::new(right)), - }))), - }) - } - _ => Err(ParseError::from_pair( - format!( - "Unexpected number of parts in logical and: {:?}", - parts.len() - ), - pair, - )), - } -} - -fn parse_equality(pair: pest::iterators::Pair) -> Result { - let mut parts = pair.clone().into_inner(); - match parts.len() { - 1 => parse_expression(parts.next().unwrap()), - 3 => { - let left = parse_expression(parts.next().unwrap())?; - let operator = parts.next().unwrap(); - let right = parse_expression(parts.next().unwrap())?; - - let operator = match operator.as_str() { - "==" => BinaryOperator::Eq, - "!=" => BinaryOperator::NotEq, - _ => panic!("Unknown equality operator: {:?}", operator), - }; - - Ok(Expression { - expr: Some(expression::Expr::Binary(Box::new(BinaryExpr { - operator: operator.into(), - left: Some(Box::new(left)), - right: Some(Box::new(right)), - }))), - }) - } - _ => Err(ParseError::from_pair( - format!("Unexpected number of parts in equality: {:?}", parts.len()), - pair, - )), - } -} - -fn parse_comparison(pair: pest::iterators::Pair) -> Result { - let mut parts = pair.clone().into_inner(); - match parts.len() { - 1 => parse_expression(parts.next().unwrap()), - 3 => { - let left = parse_expression(parts.next().unwrap())?; - let operator = parts.next().unwrap(); - let right = parse_expression(parts.next().unwrap())?; - - let operator = match operator.as_str() { - "<" => BinaryOperator::Lt, - ">" => BinaryOperator::Gt, - "<=" => BinaryOperator::Lte, - ">=" => BinaryOperator::Gte, - _ => panic!("Unknown comparison operator: {:?}", operator), - }; - - Ok(Expression { - expr: Some(expression::Expr::Binary(Box::new(BinaryExpr { - operator: operator.into(), - left: Some(Box::new(left)), - right: Some(Box::new(right)), - }))), - }) - } - _ => Err(ParseError::from_pair( - format!( - "Unexpected number of parts in comparison: {:?}", - parts.len() - ), - pair, - )), - } -} - -fn parse_additive(pair: pest::iterators::Pair) -> Result { - let mut parts = pair.clone().into_inner(); - match parts.len() { - 1 => parse_expression(parts.next().unwrap()), - 3 => { - let left = parse_expression(parts.next().unwrap())?; - let operator = parts.next().unwrap(); - let right = parse_expression(parts.next().unwrap())?; - - let operator = match operator.as_str() { - "+" => BinaryOperator::Add, - "-" => BinaryOperator::Sub, - _ => panic!("Unknown additive operator: {:?}", operator), - }; - - Ok(Expression { - expr: Some(expression::Expr::Binary(Box::new(BinaryExpr { - operator: operator.into(), - left: Some(Box::new(left)), - right: Some(Box::new(right)), - }))), - }) - } - _ => Err(ParseError::from_pair( - format!("Unexpected number of parts in additive: {:?}", parts.len()), - pair, - )), - } -} - -fn parse_multiplicative(pair: pest::iterators::Pair) -> Result { - let mut parts = pair.clone().into_inner(); - match parts.len() { - 1 => parse_expression(parts.next().unwrap()), - 3 => { - let left = parse_expression(parts.next().unwrap())?; - let operator = parts.next().unwrap(); - let right = parse_expression(parts.next().unwrap())?; - - let operator = match operator.as_str() { - "*" => BinaryOperator::Mul, - "/" => BinaryOperator::Div, - _ => panic!("Unknown multiplicative operator: {:?}", operator), - }; - - Ok(Expression { - expr: Some(expression::Expr::Binary(Box::new(BinaryExpr { - operator: operator.into(), - left: Some(Box::new(left)), - right: Some(Box::new(right)), - }))), - }) - } - _ => Err(ParseError::from_pair( - format!( - "Unexpected number of parts in multiplicative: {:?}", - parts.len() - ), - pair, - )), - } -} - -fn parse_unary(pair: pest::iterators::Pair) -> Result { - let mut parts = pair.clone().into_inner(); - match parts.len() { - 1 => parse_expression(parts.next().unwrap()), - 2 => { - let operator = parts.next().unwrap(); - let operand = parse_expression(parts.next().unwrap())?; - - let operator = match operator.as_str() { - "!" => UnaryOperator::Not, - "-" => UnaryOperator::Neg, - _ => panic!("Unknown unary operator: {:?}", operator), - }; - - Ok(Expression { - expr: Some(expression::Expr::Unary(Box::new(UnaryExpr { - operator: operator.into(), - operand: Some(Box::new(operand)), - }))), - }) - } - _ => Err(ParseError::from_pair( - format!("Unexpected number of parts in unary: {:?}", parts.len()), - pair, - )), - } -} - -fn parse_postfix(pair: pest::iterators::Pair) -> Result { - let mut parts = pair.clone().into_inner(); - match parts.len() { - 1 => parse_expression(parts.next().unwrap()), - 2 => { - let operand = parse_expression(parts.next().unwrap())?; - let argument_list = parse_argument_list(parts.next().unwrap())?; - - Ok(Expression { - expr: Some(expression::Expr::FunctionCall(Box::new(FunctionCallExpr { - function: Some(Box::new(operand)), - arguments: argument_list, - }))), - }) - } - _ => Err(ParseError::from_pair( - format!("Unexpected number of parts in postfix: {:?}", parts.len()), - pair, - )), - } -} - -fn parse_argument_list(pair: pest::iterators::Pair) -> Result, ParseError> { - let mut arguments = Vec::new(); - for p in pair.into_inner() { - let mut parts = p.into_inner(); - let identifier = parts.next().unwrap(); - let expression = parse_expression(parts.next().unwrap())?; - - arguments.push(Argument { - name: identifier.as_str().to_string(), - value: Some(expression), - }); - } - Ok(arguments) -} - -fn parse_primary(pair: pest::iterators::Pair) -> Result { - let parts = pair.into_inner(); - match parts.len() { - 1 => { - let part = parts.into_iter().next().unwrap(); - - match part.as_rule() { - Rule::expression => parse_expression(part), - Rule::literal => parse_literal(part), - Rule::identifier => parse_identifier(part), - _ => panic!("Unknown primary type: {:?}", part.as_rule()), - } - } - _ => panic!("Unexpected number of parts in primary: {:?}", parts.len()), - } -} diff --git a/klang/src/parser/functions.rs b/klang/src/parser/functions.rs deleted file mode 100644 index 4a1a24b..0000000 --- a/klang/src/parser/functions.rs +++ /dev/null @@ -1,94 +0,0 @@ -use super::ast::*; -use super::errors::ParseError; -use super::structs::Rule; -use super::statements::parse_block; -use pest::iterators::Pair; - -pub(crate) fn parse_function_def(function_def: Pair) -> Result { - let mut name = String::new(); - let mut parameters = Vec::new(); - let mut doc_string = String::new(); - let mut body = None; - - for part in function_def.into_inner() { - match part.as_rule() { - Rule::identifier => name = part.as_str().to_string(), - Rule::parameter_list => parameters = parse_parameters(part)?, - Rule::doc_string => doc_string = parse_doc_string(part)?, - Rule::block => body = Some(parse_block(part)?), - _ => { - return Err(ParseError::from_pair( - format!("Unknown rule: {:?}", part.as_rule()), - part, - )); - } - } - } - - Ok(FunctionDef { - name, - parameters, - doc_string, - body, - }) -} - -fn parse_parameters(parameters: Pair) -> Result, ParseError> { - let mut result = Vec::new(); - - for part in parameters.into_inner() { - match part.as_rule() { - Rule::parameter_value => { - result.push(parse_parameter_value(part)?); - } - _ => { - return Err(ParseError::from_pair( - format!("Unknown rule: {:?}", part.as_rule()), - part, - )); - } - } - } - - Ok(result) -} - -fn parse_parameter_value(parameter_value: Pair) -> Result { - let mut identifier = String::new(); - let mut param_type = String::new(); - - for part in parameter_value.into_inner() { - match part.as_rule() { - Rule::identifier => identifier = part.as_str().to_string(), - Rule::parameter_type => param_type = part.as_str().to_string(), - _ => { - return Err(ParseError::from_pair( - format!("Unknown rule: {:?}", part.as_rule()), - part, - )); - } - } - } - - let parsed_type = match param_type.as_str() { - "string" => ParameterType::String, - "number" => ParameterType::Number, - "boolean" => ParameterType::Boolean, - _ => { - return Err(ParseError::new(format!( - "Unknown parameter type: {:?}", - param_type - ))); - } - }; - - Ok(Parameter { - name: identifier, - r#type: parsed_type.into(), - }) -} - -fn parse_doc_string(pair: Pair) -> Result { - let inner = pair.into_inner().next().unwrap(); - Ok(inner.as_str()[1..inner.as_str().len() - 1].to_string()) -} diff --git a/klang/src/parser/lang.rs b/klang/src/parser/lang.rs new file mode 100644 index 0000000..4293dc3 --- /dev/null +++ b/klang/src/parser/lang.rs @@ -0,0 +1,77 @@ +use super::errors::ParseError; +use super::ir::*; +use super::structs::Rule; +use crate::parser::passes::ir_to_ast; +use crate::parser::KlangProgram; +use pest::iterators::Pair; + +pub fn parse_program(pair: pest::iterators::Pair) -> Result { + let mut all_commands = Vec::new(); + + for command_pair in pair.into_inner() { + match command_pair.as_rule() { + Rule::line => match parse_line(command_pair) { + Ok(commands) => all_commands.extend(commands), + Err(e) => return Err(e), + }, + Rule::EOI => break, + _ => { + return Err(ParseError::from_pair( + format!("Unknown rule: {:?}", command_pair.as_rule()), + command_pair, + )); + } + } + } + + let ir_program = Program { + commands: all_commands, + }; + let ast_program = ir_to_ast(&ir_program)?; + + Ok(KlangProgram { + program: ast_program, + }) +} + +fn parse_line(line: Pair) -> Result, ParseError> { + line.into_inner() + .map(|command_pair| match command_pair.as_rule() { + Rule::function => Some(parse_function(command_pair)), + Rule::function_call => Some(parse_function_call(command_pair)), + Rule::command => Some(parse_command(command_pair)), + Rule::empty_line => None, + _ => Some(Err(ParseError::from_pair( + format!("Unknown rule: {:?}", command_pair.as_rule()), + command_pair, + ))), + }) + .filter_map(|command| command) + .collect() +} + +fn parse_function(pair: Pair) -> Result { + let mut inner = pair.into_inner(); + let text = inner.next().unwrap().as_str().to_string(); + + let mut children = Vec::new(); + for child in inner { + children.extend(parse_line(child)?); + } + + Ok(Command { text, children }) +} + +fn parse_function_call(pair: Pair) -> Result { + Ok(Command { + text: pair.as_str().to_string(), + children: Vec::new(), + }) +} + +fn parse_command(pair: Pair) -> Result { + Ok(Command { + text: pair.as_str().to_string(), + children: Vec::new(), + }) +} diff --git a/klang/src/parser/literals.rs b/klang/src/parser/literals.rs deleted file mode 100644 index d056fcd..0000000 --- a/klang/src/parser/literals.rs +++ /dev/null @@ -1,86 +0,0 @@ -use super::ast::*; -use super::errors::ParseError; -use super::structs::Rule; - -pub(crate) fn parse_literal(pair: pest::iterators::Pair) -> Result { - let inner_pair = pair.into_inner().next().unwrap(); - match inner_pair.as_rule() { - Rule::string => { - let value = inner_pair.as_str()[1..inner_pair.as_str().len() - 1].to_string(); - Ok(Expression { - expr: Some(expression::Expr::Literal(LiteralExpr { - value: Some(literal_expr::Value::StringLiteral(StringLiteral { value })), - })), - }) - } - Rule::number => { - let s = inner_pair.as_str(); - let (value_str, unit) = s.trim().split_at( - s.find(|c: char| !c.is_ascii_digit() && c != '.' && c != '-') - .unwrap_or(s.len()), - ); - let value: f64 = value_str.parse().unwrap(); - - // If the unit is provided, use it to convert to standard units. - let value = if unit.is_empty() { - value - } else { - convert_to_standard_units(value, unit) - }; - - Ok(Expression { - expr: Some(expression::Expr::Literal(LiteralExpr { - value: Some(literal_expr::Value::NumberLiteral(NumberLiteral { value })), - })), - }) - } - Rule::boolean => { - let value = inner_pair.as_str() == "true"; - Ok(Expression { - expr: Some(expression::Expr::Literal(LiteralExpr { - value: Some(literal_expr::Value::BooleanLiteral(BooleanLiteral { - value, - })), - })), - }) - } - _ => Err(ParseError::from_pair( - format!("Unknown literal type: {:?}", inner_pair), - inner_pair, - )), - } -} - -fn convert_to_standard_units(value: f64, unit: &str) -> f64 { - match unit { - // Convert lengths to meters. - "mm" => value * 0.001, - "cm" => value * 0.01, - "m" => value, - "km" => value * 1000.0, - "in" => value * 0.0254, - "ft" => value * 0.3048, - "yd" => value * 0.9144, - "mi" => value * 1609.34, - // Convert time to seconds. - "ms" => value * 0.001, - "s" => value, - "min" => value * 60.0, - "hr" => value * 3600.0, - // Convert angles to degrees. - "deg" => value, - "rad" => value * (180.0 / std::f64::consts::PI), - // Error if the unit is unknown. - _ => panic!("Unknown unit: {:?}", unit), - } -} - -pub(crate) fn parse_identifier( - pair: pest::iterators::Pair, -) -> Result { - Ok(Expression { - expr: Some(expression::Expr::Identifier(Identifier { - name: pair.as_str().to_string(), - })), - }) -} diff --git a/klang/src/parser/mod.rs b/klang/src/parser/mod.rs index 0d5969d..e5408f9 100644 --- a/klang/src/parser/mod.rs +++ b/klang/src/parser/mod.rs @@ -2,46 +2,22 @@ mod ast { include!(concat!(env!("OUT_DIR"), "/proto/ast.rs")); } -use ast::*; +mod ir { + include!(concat!(env!("OUT_DIR"), "/proto/ir.rs")); +} pub mod errors; -pub mod expressions; -pub mod functions; -pub mod literals; -pub mod statements; +pub mod lang; +pub mod passes; pub mod structs; use errors::ParseError; -use functions::parse_function_def; +use lang::parse_program; use pest::Parser; use std::fs; use std::path::Path; use structs::{KlangProgram, PestParser, Rule}; -pub fn parse_program(pair: pest::iterators::Pair) -> Result { - let mut functions = Vec::new(); - - for function_pair in pair.into_inner() { - match function_pair.as_rule() { - Rule::function_def => match parse_function_def(function_pair) { - Ok(function) => functions.push(function), - Err(e) => return Err(e), - }, - Rule::EOI => break, - _ => { - return Err(ParseError::from_pair( - format!("Unknown rule: {:?}", function_pair.as_rule()), - function_pair, - )); - } - } - } - - Ok(KlangProgram { - program: Program { functions }, - }) -} - pub fn parse_string(input: &str) -> Result { match PestParser::parse(Rule::program, input) { Ok(mut pairs) => parse_program(pairs.next().unwrap()), @@ -61,23 +37,38 @@ pub fn parse_file(file_path: &Path) -> Result { parse_string(&unparsed_file) } -pub fn write_program_to_file(program: &KlangProgram, file_path: &Path) -> Result<(), ParseError> { - let mut buf = Vec::new(); - prost::Message::encode(&program.program, &mut buf).map_err(|e| { - ParseError::new(format!( - "Error encoding program to file '{}': {}", - file_path.display(), - e - )) - })?; +pub fn write_program_to_file( + program: &KlangProgram, + file_path: &Path, + binary: bool, +) -> Result<(), ParseError> { + if binary { + let mut buf = Vec::new(); + prost::Message::encode(&program.program, &mut buf).map_err(|e| { + ParseError::new(format!( + "Error encoding program to file '{}': {}", + file_path.display(), + e + )) + })?; - fs::write(file_path, &buf).map_err(|e| { - ParseError::new(format!( - "Error writing program to file '{}': {}", - file_path.display(), - e - )) - })?; + fs::write(file_path, &buf).map_err(|e| { + ParseError::new(format!( + "Error writing program to file '{}': {}", + file_path.display(), + e + )) + })?; + } else { + let program_str = format!("{:#?}", program.program); + fs::write(file_path, &program_str).map_err(|e| { + ParseError::new(format!( + "Error writing program to file '{}': {}", + file_path.display(), + e + )) + })?; + } Ok(()) } diff --git a/klang/src/parser/passes/mod.rs b/klang/src/parser/passes/mod.rs new file mode 100644 index 0000000..24498e3 --- /dev/null +++ b/klang/src/parser/passes/mod.rs @@ -0,0 +1,96 @@ +use super::ast::{Command as AstCommand, Program as AstProgram}; +use super::errors::ParseError; +use super::ir::line::LineKind; +use super::ir::{Command, Function, Line, Program}; +use std::collections::HashMap; + +pub(crate) fn ir_to_ast(ir_program: &Program) -> Result { + let mut functions = HashMap::new(); + for command in &ir_program.commands { + let line = Line { + line_kind: Some(LineKind::Command(command.clone())), + }; + collect_functions(&line, &mut functions)?; + } + + let mut ast_program = AstProgram { + commands: Vec::new(), + }; + let mut call_stack = Vec::new(); + for command in &ir_program.commands { + let line = Line { + line_kind: Some(LineKind::Command(command.clone())), + }; + let mut commands = process_line(&line, &functions, &mut call_stack)?; + ast_program.commands.append(&mut commands); + } + Ok(ast_program) +} + +fn collect_functions( + line: &Line, + functions: &mut HashMap, +) -> Result<(), ParseError> { + if let Some(kind) = &line.line_kind { + match kind { + LineKind::Function(func) => { + functions.insert(func.name.clone(), func.clone()); + for inner_line in &func.lines { + collect_functions(inner_line, functions)?; // Note: pass inner_line directly + } + Ok(()) + } + _ => Ok(()), + } + } else { + Ok(()) + } +} + +fn process_line( + line: &Line, + functions: &HashMap, + call_stack: &mut Vec, +) -> Result, ParseError> { + if let Some(kind) = &line.line_kind { + match kind { + LineKind::Function { .. } => Ok(Vec::new()), + LineKind::FunctionCall(func_call) => { + let name = &func_call.name; + if call_stack.contains(name) { + panic!("Recursive function call detected: {}", name); + } + if let Some(func) = functions.get(name) { + call_stack.push(name.clone()); + let mut commands = Vec::new(); + for inner_line in &func.lines { + let mut cmds = process_line(inner_line, functions, call_stack)?; + commands.append(&mut cmds); + } + call_stack.pop(); + Ok(commands) + } else { + Ok(Vec::new()) + } + } + LineKind::Command(cmd) => { + let ast_command = process_command(cmd)?; + Ok(vec![ast_command]) + } + } + } else { + Ok(Vec::new()) + } +} + +fn process_command(cmd: &Command) -> Result { + let mut children = Vec::new(); + for child in &cmd.children { + let child_cmd = process_command(child)?; + children.push(child_cmd); + } + Ok(AstCommand { + text: cmd.text.clone(), + children, + }) +} diff --git a/klang/src/parser/statements.rs b/klang/src/parser/statements.rs deleted file mode 100644 index 9b784ce..0000000 --- a/klang/src/parser/statements.rs +++ /dev/null @@ -1,117 +0,0 @@ -use super::ast::*; -use super::errors::ParseError; -use super::expressions::parse_expression; -use super::structs::Rule; -use pest::iterators::Pair; - -pub fn parse_block(pair: Pair) -> Result { - let statements = pair - .into_inner() - .filter_map(|p| { - if p.as_rule() == Rule::statement { - Some(parse_statement(p)) - } else { - None - } - }) - .collect::, _>>()?; - - Ok(Block { statements }) -} - -pub fn parse_statement(pair: Pair) -> Result { - let inner = pair.into_inner().next().unwrap(); - match inner.as_rule() { - Rule::assignment_stmt => parse_assignment_statement(inner), - Rule::expression_stmt => parse_expression_statement(inner), - Rule::loop_stmt => parse_loop_statement(inner), - Rule::break_stmt => parse_break_statement(inner), - Rule::return_stmt => parse_return_statement(inner), - Rule::empty_stmt => parse_empty_statement(inner), - _ => Err(ParseError::from_pair( - format!("Unknown statement type: {:?}", inner.as_rule()), - inner, - )), - } -} - -fn parse_assignment_statement(pair: Pair) -> Result { - let mut inner = pair.clone().into_inner(); - let identifier = inner.next().unwrap().as_str().to_string(); - let operator = inner.next().unwrap().as_str().to_string(); - let expression = parse_expression(inner.next().unwrap())?; - - let parsed_operator = match operator.as_str() { - "=" => None, - "+=" => Some(BinaryOperator::Add), - "-=" => Some(BinaryOperator::Sub), - "*=" => Some(BinaryOperator::Mul), - "/=" => Some(BinaryOperator::Div), - _ => { - return Err(ParseError::from_pair( - format!("Unknown assignment operator: {:?}", operator), - pair, - )) - } - }; - - Ok(match parsed_operator { - Some(op) => Statement { - stmt: Some(statement::Stmt::Assignment(AssignmentStmt { - identifier: identifier.clone(), - expression: Some(Expression { - expr: Some(expression::Expr::Binary(Box::new(BinaryExpr { - operator: op.into(), - left: Some(Box::new(Expression { - expr: Some(expression::Expr::Identifier(Identifier { - name: identifier, - })), - })), - right: Some(Box::new(expression)), - }))), - }), - })), - }, - None => Statement { - stmt: Some(statement::Stmt::Assignment(AssignmentStmt { - identifier, - expression: Some(expression), - })), - }, - }) -} - -fn parse_expression_statement(pair: Pair) -> Result { - let expression = parse_expression(pair.into_inner().next().unwrap())?; - Ok(Statement { - stmt: Some(statement::Stmt::Expression(ExpressionStmt { - expression: Some(expression), - })), - }) -} - -fn parse_loop_statement(pair: Pair) -> Result { - let block = parse_block(pair.into_inner().next().unwrap())?; - Ok(Statement { - stmt: Some(statement::Stmt::Loop(LoopStmt { body: Some(block) })), - }) -} - -fn parse_break_statement(_pair: Pair) -> Result { - Ok(Statement { - stmt: Some(statement::Stmt::Break(BreakStmt {})), - }) -} - -fn parse_return_statement(pair: Pair) -> Result { - let expression = parse_expression(pair.into_inner().next().unwrap())?; - Ok(Statement { - stmt: Some(statement::Stmt::Return(ReturnStmt { - expression: Some(expression), - })), - }) -} - -fn parse_empty_statement(_pair: Pair) -> Result { - Ok(Statement { stmt: None }) -} diff --git a/klang/src/pest/klang.pest b/klang/src/pest/klang.pest index ea6cf25..697bc66 100644 --- a/klang/src/pest/klang.pest +++ b/klang/src/pest/klang.pest @@ -1,106 +1,13 @@ -WHITESPACE = _{ " " | "\t" | "\r" | "\n" } +WHITESPACE = _{ " " | "\t" } COMMENT = _{ "//" ~ (!("\n" | "\r") ~ ANY)* ~ ("\r"? ~ "\n" | EOI) | "/*" ~ (!"*/" ~ ANY)* ~ "*/" } -EOL = _{ ";" } - -// Identifiers -// ----------- - -identifier = @{ ASCII_ALPHA ~ (ASCII_ALPHANUMERIC | "_")* } - -// Literals -// -------- - -literal = { string | number | boolean } - -string_char = { "\\" ~ ANY | !("\"") ~ ANY } - -string = @{ "\"" ~ string_char* ~ "\"" } - -number = @{ - "-"? ~ ASCII_DIGIT+ ~ ("." ~ ASCII_DIGIT+)? ~ unit? -} - -unit = @{ - "mm" | "cm" | "m" | "km" | - "in" | "ft" | "yd" | "mi" | - "ms" | "s" | "min" | "hr" | - "deg" | "rad" -} - -boolean = @{ "true" | "false" } - -// Statements -// ---------- - -block = { "{" ~ statement* ~ "}" } - -statement = { - assignment_stmt | - expression_stmt | - loop_stmt | - break_stmt | - return_stmt | - empty_stmt -} - -empty_stmt = { EOL } - -// Assignments -// ----------- - -assignment_stmt = { "let" ~ identifier ~ assign_op ~ expression ~ EOL } -assign_op = { "=" | "+=" | "-=" | "*=" | "/=" } - -// Control Flow -// ------------ - -loop_stmt = { "loop" ~ block ~ EOL } -break_stmt = { "break" ~ EOL } -return_stmt = { "return" ~ expression ~ EOL } - -// Expressions -// ----------- - -expression_stmt = { expression ~ EOL } -expression = { conditional } -conditional = { logical_or ~ ( "?" ~ expression ~ ":" ~ expression )? } -logical_or = { logical_and ~ ( "||" ~ logical_and )* } -logical_and = { equality ~ ( "&&" ~ equality )* } -equality = { comparison ~ ( equality_op ~ comparison )* } -equality_op = { "==" | "!=" } -comparison = { multiplicative ~ ( comparison_op ~ multiplicative )* } -comparison_op = { "<=" | ">=" | "<" | ">" } -multiplicative = { additive ~ ( multiplicative_op ~ multiplicative )? } -multiplicative_op = { "*" | "/" } -additive = { unary ~ ( additive_op ~ additive )? } -additive_op = { "+" | "-" } -unary = { unary_op* ~ postfix } -unary_op = { "!" | "-" } -postfix = { primary ~ ( argument_list )? } -primary = { "(" ~ expression ~ ")" | literal | identifier } - -// Function calls -// -------------- - -argument_list = { "(" ~ (argument ~ ("," ~ argument)*)? ~ ")" } -argument = { identifier ~ ":" ~ expression } - -// Functions -// --------- - -function_def = { - "fn" ~ identifier ~ "(" ~ parameter_list? ~ ")" ~ doc_string? ~ block -} -parameter_list = { parameter_value ~ ("," ~ parameter_value)* } -doc_string = { ":" ~ string } -parameter_type = @{ "string" | "number" | "boolean" } -parameter_value = { identifier ~ ":" ~ parameter_type } - -// Program -// ------- - -program = { SOI ~ function_def* ~ EOI } +text = { ( ASCII_ALPHANUMERIC | "," | "." | "!" | "?" | "%" | " " | "[" | "]" )+ } +command = { text } +function = { ">" ~ text ~ "{" ~ line* ~ "}" } +function_call = { "\"" ~ text } +empty_line = { NEWLINE } +line = { function | function_call | command | empty_line } +program = { SOI ~ line* ~ EOI } diff --git a/klang/src/proto/ast.proto b/klang/src/proto/ast.proto index c5227d7..3c615ae 100644 --- a/klang/src/proto/ast.proto +++ b/klang/src/proto/ast.proto @@ -3,144 +3,10 @@ syntax = "proto3"; package ast; message Program { - repeated FunctionDef functions = 1; + repeated Command commands = 1; } -message FunctionDef { - string name = 1; - repeated Parameter parameters = 2; - string doc_string = 3; - Block body = 4; -} - -message Parameter { - string name = 1; - ParameterType type = 2; -} - -enum ParameterType { - String = 0; - Number = 1; - Boolean = 2; -} - -message Block { - repeated Statement statements = 1; -} - -message Statement { - oneof stmt { - AssignmentStmt assignment = 1; - ExpressionStmt expression = 2; - LoopStmt loop = 3; - BreakStmt break = 4; - ReturnStmt return = 5; - } -} - -message AssignmentStmt { - string identifier = 1; - Expression expression = 2; -} - -message ExpressionStmt { - Expression expression = 1; -} - -message LoopStmt { - Block body = 1; -} - -message BreakStmt { -} - -message ReturnStmt { - Expression expression = 1; -} - -message Expression { - oneof expr { - ConditionalExpr conditional = 1; - BinaryExpr binary = 2; - UnaryExpr unary = 3; - FunctionCallExpr function_call = 4; - LiteralExpr literal = 5; - Identifier identifier = 6; - GroupingExpr grouping = 7; - } -} - -message ConditionalExpr { - Expression condition = 1; - Expression then_expr = 2; - Expression else_expr = 3; -} - -message BinaryExpr { - Expression left = 1; - BinaryOperator operator = 2; - Expression right = 3; -} - -enum BinaryOperator { - Add = 0; - Sub = 1; - Mul = 2; - Div = 3; - Eq = 4; - NotEq = 5; - Lt = 6; - Gt = 7; - Lte = 8; - Gte = 9; - And = 10; - Or = 11; -} - -message UnaryExpr { - UnaryOperator operator = 1; - Expression operand = 2; -} - -enum UnaryOperator { - Not = 0; - Neg = 1; -} - -message FunctionCallExpr { - Expression function = 1; - repeated Argument arguments = 2; -} - -message LiteralExpr { - oneof value { - StringLiteral string_literal = 1; - NumberLiteral number_literal = 2; - BooleanLiteral boolean_literal = 3; - } -} - -message StringLiteral { - string value = 1; -} - -message NumberLiteral { - double value = 1; -} - -message BooleanLiteral { - bool value = 1; -} - -message Identifier { - string name = 1; -} - -message GroupingExpr { - Expression expression = 1; -} - -message Argument { - string name = 1; - Expression value = 2; +message Command { + string text = 1; + repeated Command children = 2; } diff --git a/klang/src/proto/ir.proto b/klang/src/proto/ir.proto new file mode 100644 index 0000000..4b478ce --- /dev/null +++ b/klang/src/proto/ir.proto @@ -0,0 +1,30 @@ +syntax = "proto3"; + +package ir; + +message Program { + repeated Command commands = 1; +} + +message Line { + oneof line_kind { + Function function = 1; + FunctionCall function_call = 2; + Command command = 3; + } +} + +message Function { + string name = 1; + repeated Line lines = 2; +} + +message FunctionCall { + string name = 1; + repeated string arguments = 2; +} + +message Command { + string text = 1; + repeated Command children = 2; +} From acca6ac9590e01aa25cd2b10bb7441c04106ad3b Mon Sep 17 00:00:00 2001 From: Benjamin Bolte Date: Wed, 30 Oct 2024 22:34:32 -0700 Subject: [PATCH 2/7] work in progress... --- examples/simple.k | 11 +- klang/src/parser/lang.rs | 123 ++++++++++++----- klang/src/parser/passes/mod.rs | 238 ++++++++++++++++++++++++--------- klang/src/pest/klang.pest | 18 ++- klang/src/proto/ir.proto | 25 +++- 5 files changed, 306 insertions(+), 109 deletions(-) diff --git a/examples/simple.k b/examples/simple.k index 95d6f7a..f04b9cb 100644 --- a/examples/simple.k +++ b/examples/simple.k @@ -1,7 +1,12 @@ > wave [arm] arm { - move [arm] joint 1 to 90 - move [arm] joint 2 to 90 - move [arm] joint 3 to 90 + > wave [arm] joint [joint] twice { + move [arm] joint [joint] to 90 + move [arm] joint [joint] to 0 + } + + wave [arm] joint 1 twice + wave [arm] joint 2 twice + wave [arm] joint 3 twice } > wave both arms { diff --git a/klang/src/parser/lang.rs b/klang/src/parser/lang.rs index 4293dc3..d14d904 100644 --- a/klang/src/parser/lang.rs +++ b/klang/src/parser/lang.rs @@ -1,32 +1,33 @@ use super::errors::ParseError; -use super::ir::*; +use super::ir::{ + line::LineKind, text_part::PartKind, Command, Function, FunctionArg, FunctionCall, Line, + Program, TextPart, TextWithArgs, +}; use super::structs::Rule; use crate::parser::passes::ir_to_ast; use crate::parser::KlangProgram; use pest::iterators::Pair; pub fn parse_program(pair: pest::iterators::Pair) -> Result { - let mut all_commands = Vec::new(); + let mut all_lines = Vec::new(); - for command_pair in pair.into_inner() { - match command_pair.as_rule() { - Rule::line => match parse_line(command_pair) { - Ok(commands) => all_commands.extend(commands), + for line_pair in pair.into_inner() { + match line_pair.as_rule() { + Rule::line => match parse_line(line_pair) { + Ok(lines) => all_lines.extend(lines), Err(e) => return Err(e), }, Rule::EOI => break, _ => { return Err(ParseError::from_pair( - format!("Unknown rule: {:?}", command_pair.as_rule()), - command_pair, + format!("Unknown rule: {:?}", line_pair.as_rule()), + line_pair, )); } } } - let ir_program = Program { - commands: all_commands, - }; + let ir_program = Program { lines: all_lines }; let ast_program = ir_to_ast(&ir_program)?; Ok(KlangProgram { @@ -34,44 +35,98 @@ pub fn parse_program(pair: pest::iterators::Pair) -> Result) -> Result, ParseError> { +fn parse_line(line: Pair) -> Result, ParseError> { line.into_inner() - .map(|command_pair| match command_pair.as_rule() { - Rule::function => Some(parse_function(command_pair)), - Rule::function_call => Some(parse_function_call(command_pair)), - Rule::command => Some(parse_command(command_pair)), + .map(|line_pair| match line_pair.as_rule() { + Rule::function_def => match parse_function_def(line_pair) { + Ok(func) => Some(Ok(Line { + line_kind: Some(LineKind::Function(func)), + })), + Err(e) => Some(Err(e)), + }, + Rule::function_call => match parse_function_call(line_pair) { + Ok(call) => Some(Ok(Line { + line_kind: Some(LineKind::FunctionCall(call)), + })), + Err(e) => Some(Err(e)), + }, + Rule::command => match parse_command(line_pair) { + Ok(cmd) => Some(Ok(Line { + line_kind: Some(LineKind::Command(cmd)), + })), + Err(e) => Some(Err(e)), + }, Rule::empty_line => None, _ => Some(Err(ParseError::from_pair( - format!("Unknown rule: {:?}", command_pair.as_rule()), - command_pair, + format!("Unknown rule: {:?}", line_pair.as_rule()), + line_pair, ))), }) - .filter_map(|command| command) + .filter_map(|line| line) .collect() } -fn parse_function(pair: Pair) -> Result { - let mut inner = pair.into_inner(); - let text = inner.next().unwrap().as_str().to_string(); - - let mut children = Vec::new(); - for child in inner { - children.extend(parse_line(child)?); - } +fn parse_function_def(pair: Pair) -> Result { + let text_with_args = parse_text_with_args(pair.clone().into_inner().next().unwrap())?; + let children: Result, ParseError> = pair + .into_inner() + .skip(1) + .map(parse_line) + .try_fold(Vec::new(), |mut acc, result| { + acc.extend(result?); + Ok(acc) + }); - Ok(Command { text, children }) + Ok(Function { + name: Some(text_with_args), + lines: children?, + }) } -fn parse_function_call(pair: Pair) -> Result { - Ok(Command { - text: pair.as_str().to_string(), - children: Vec::new(), +fn parse_function_call(pair: Pair) -> Result { + Ok(FunctionCall { + name: Some(parse_text_with_args(pair.into_inner().next().unwrap())?), }) } fn parse_command(pair: Pair) -> Result { Ok(Command { - text: pair.as_str().to_string(), - children: Vec::new(), + text: Some(parse_text_with_args(pair.into_inner().next().unwrap())?), }) } + +fn parse_text_with_args(pair: Pair) -> Result { + match pair.as_rule() { + Rule::text_with_function_args | Rule::text_with_function_params => { + let parts = pair + .into_inner() + .map(parse_text_part) + .collect::, ParseError>>()?; + Ok(TextWithArgs { parts }) + } + _ => Err(ParseError::from_pair( + format!("Expected text with args, got {:?}", pair.as_rule()), + pair, + )), + } +} + +fn parse_text_part(pair: Pair) -> Result { + match pair.as_rule() { + Rule::text => Ok(TextPart { + part_kind: Some(PartKind::Text(pair.as_str().to_string())), + }), + Rule::function_arg | Rule::function_param => { + let func_arg = FunctionArg { + text: pair.into_inner().next().unwrap().as_str().to_string(), + }; + Ok(TextPart { + part_kind: Some(PartKind::FunctionArg(func_arg)), + }) + } + _ => Err(ParseError::from_pair( + format!("Expected text or function arg, got {:?}", pair.as_rule()), + pair, + )), + } +} diff --git a/klang/src/parser/passes/mod.rs b/klang/src/parser/passes/mod.rs index 24498e3..ae1d904 100644 --- a/klang/src/parser/passes/mod.rs +++ b/klang/src/parser/passes/mod.rs @@ -1,80 +1,123 @@ use super::ast::{Command as AstCommand, Program as AstProgram}; use super::errors::ParseError; -use super::ir::line::LineKind; -use super::ir::{Command, Function, Line, Program}; +use super::ir::{ + line::LineKind, text_part::PartKind, Command, Function, Line, Program, TextWithArgs, +}; use std::collections::HashMap; -pub(crate) fn ir_to_ast(ir_program: &Program) -> Result { - let mut functions = HashMap::new(); - for command in &ir_program.commands { - let line = Line { - line_kind: Some(LineKind::Command(command.clone())), - }; - collect_functions(&line, &mut functions)?; - } - - let mut ast_program = AstProgram { - commands: Vec::new(), - }; - let mut call_stack = Vec::new(); - for command in &ir_program.commands { - let line = Line { - line_kind: Some(LineKind::Command(command.clone())), - }; - let mut commands = process_line(&line, &functions, &mut call_stack)?; - ast_program.commands.append(&mut commands); +fn get_function_signature(name: &TextWithArgs) -> (String, Vec) { + let mut signature = String::new(); + let mut params = Vec::new(); + let mut first = true; + for part in &name.parts { + if first { + first = false; + } else { + signature.push_str(" "); + } + match &part.part_kind { + Some(PartKind::Text(text)) => { + signature.push_str(text); + } + Some(PartKind::FunctionArg(arg)) => { + signature.push_str("["); + signature.push_str(&arg.text); + signature.push_str("]"); + params.push(arg.text.clone()); + } + None => {} + } } - Ok(ast_program) + (signature, params) } -fn collect_functions( - line: &Line, - functions: &mut HashMap, -) -> Result<(), ParseError> { - if let Some(kind) = &line.line_kind { - match kind { - LineKind::Function(func) => { - functions.insert(func.name.clone(), func.clone()); - for inner_line in &func.lines { - collect_functions(inner_line, functions)?; // Note: pass inner_line directly +fn match_function_call( + call_name: &TextWithArgs, + func_signature: &TextWithArgs, +) -> Option> { + let mut args = HashMap::new(); + let mut call_iter = call_name.parts.iter(); + let mut sig_iter = func_signature.parts.iter(); + + loop { + match (call_iter.next(), sig_iter.next()) { + (Some(call_part), Some(sig_part)) => { + match (&call_part.part_kind, &sig_part.part_kind) { + (Some(PartKind::Text(call_text)), Some(PartKind::Text(sig_text))) => { + if call_text != sig_text { + return None; + } + } + (Some(PartKind::Text(call_text)), Some(PartKind::FunctionArg(arg))) => { + args.insert(arg.text.clone(), call_text.clone()); + } + ( + Some(PartKind::FunctionArg(call_arg)), + Some(PartKind::FunctionArg(sig_arg)), + ) => { + args.insert(sig_arg.text.clone(), call_arg.text.clone()); + } + _ => { + return None; + } } - Ok(()) } - _ => Ok(()), + (None, None) => break, + _ => return None, } - } else { - Ok(()) } + Some(args) } -fn process_line( +fn process_line_with_args( line: &Line, - functions: &HashMap, + functions: &HashMap)>, call_stack: &mut Vec, + arg_map: &HashMap, ) -> Result, ParseError> { if let Some(kind) = &line.line_kind { match kind { LineKind::Function { .. } => Ok(Vec::new()), LineKind::FunctionCall(func_call) => { - let name = &func_call.name; - if call_stack.contains(name) { - panic!("Recursive function call detected: {}", name); - } - if let Some(func) = functions.get(name) { - call_stack.push(name.clone()); - let mut commands = Vec::new(); - for inner_line in &func.lines { - let mut cmds = process_line(inner_line, functions, call_stack)?; - commands.append(&mut cmds); + if let Some(name) = &func_call.name { + let (call_signature, _) = get_function_signature(name); + for (func_sig, (func_def, _)) in functions { + if let Some(name_def) = &func_def.name { + if let Some(arg_map) = match_function_call(name, name_def) { + if call_stack.contains(func_sig) { + return Err(ParseError::new(format!( + "Recursive function call: {}", + func_sig + ))); + } + call_stack.push(func_sig.clone()); + let mut commands = Vec::new(); + for inner_line in &func_def.lines { + let mut cmds = process_line_with_args( + inner_line, functions, call_stack, &arg_map, + )?; + commands.append(&mut cmds); + } + call_stack.pop(); + return Ok(commands); + } + } } - call_stack.pop(); - Ok(commands) + Err(ParseError::new(format!( + "Function not found: {{ {} }} Available functions: {{ {} }}", + call_signature, + functions + .keys() + .map(|k| k.as_str()) + .collect::>() + .join(", ") + ))) } else { - Ok(Vec::new()) + Err(ParseError::new("Function call without name".to_string())) } } LineKind::Command(cmd) => { - let ast_command = process_command(cmd)?; + let ast_command = process_command_with_args(cmd, arg_map)?; Ok(vec![ast_command]) } } @@ -83,14 +126,87 @@ fn process_line( } } -fn process_command(cmd: &Command) -> Result { - let mut children = Vec::new(); - for child in &cmd.children { - let child_cmd = process_command(child)?; - children.push(child_cmd); +fn process_command_with_args( + cmd: &Command, + arg_map: &HashMap, +) -> Result { + if let Some(text) = &cmd.text { + let text = substitute_text_with_args(text, arg_map)?; + Ok(AstCommand { + text, + children: Vec::new(), // Removed children handling since field doesn't exist + }) + } else { + Err(ParseError::new("Command without text".to_string())) + } +} + +fn substitute_text_with_args( + text_with_args: &TextWithArgs, + arg_map: &HashMap, +) -> Result { + let mut result = String::new(); + let mut first = true; + for part in &text_with_args.parts { + if first { + first = false; + } else { + result.push_str(" "); + } + match &part.part_kind { + Some(PartKind::Text(text)) => { + result.push_str(text); + } + Some(PartKind::FunctionArg(arg)) => { + if let Some(value) = arg_map.get(&arg.text) { + result.push_str(value); + } else { + return Err(ParseError::new(format!("Argument not found: {}", arg.text))); + } + } + None => {} + } + } + Ok(result) +} + +fn collect_functions( + line: &Line, + functions: &mut HashMap)>, +) -> Result<(), ParseError> { + if let Some(kind) = &line.line_kind { + match kind { + LineKind::Function(func) => { + if let Some(name) = &func.name { + let (signature, params) = get_function_signature(name); + functions.insert(signature.clone(), (func.clone(), params)); + for inner_line in &func.lines { + collect_functions(inner_line, functions)?; + } + } + Ok(()) + } + _ => Ok(()), + } + } else { + Ok(()) + } +} + +pub(crate) fn ir_to_ast(ir_program: &Program) -> Result { + let mut functions = HashMap::new(); + for line in &ir_program.lines { + collect_functions(line, &mut functions)?; + } + + let mut ast_program = AstProgram { + commands: Vec::new(), + }; + let mut call_stack = Vec::new(); + for line in &ir_program.lines { + let mut commands = + process_line_with_args(line, &functions, &mut call_stack, &HashMap::new())?; + ast_program.commands.append(&mut commands); } - Ok(AstCommand { - text: cmd.text.clone(), - children, - }) + Ok(ast_program) } diff --git a/klang/src/pest/klang.pest b/klang/src/pest/klang.pest index 697bc66..286867a 100644 --- a/klang/src/pest/klang.pest +++ b/klang/src/pest/klang.pest @@ -4,10 +4,18 @@ COMMENT = _{ "/*" ~ (!"*/" ~ ANY)* ~ "*/" } -text = { ( ASCII_ALPHANUMERIC | "," | "." | "!" | "?" | "%" | " " | "[" | "]" )+ } -command = { text } -function = { ">" ~ text ~ "{" ~ line* ~ "}" } -function_call = { "\"" ~ text } +text = { ( ASCII_ALPHANUMERIC | "," | "." | "!" | "?" | "%" | " " )+ } + +function_arg = { "[" ~ text ~ "]" } +text_with_function_args = { (text | function_arg)+ } + +function_param = { "[" ~ text ~ "]" } +text_with_function_params = { (text | function_param)+ } + +command = { text_with_function_args } +function_def = { ">" ~ text_with_function_params ~ "{" ~ line* ~ "}" } +function_call = { "\"" ~ text_with_function_args } + empty_line = { NEWLINE } -line = { function | function_call | command | empty_line } +line = { function_def | function_call | command | empty_line } program = { SOI ~ line* ~ EOI } diff --git a/klang/src/proto/ir.proto b/klang/src/proto/ir.proto index 4b478ce..b9eb774 100644 --- a/klang/src/proto/ir.proto +++ b/klang/src/proto/ir.proto @@ -3,7 +3,7 @@ syntax = "proto3"; package ir; message Program { - repeated Command commands = 1; + repeated Line lines = 1; } message Line { @@ -14,17 +14,30 @@ message Line { } } +message TextWithArgs { + repeated TextPart parts = 1; +} + +message TextPart { + oneof part_kind { + string text = 1; + FunctionArg function_arg = 2; + } +} + +message FunctionArg { + string text = 1; +} + message Function { - string name = 1; + TextWithArgs name = 1; repeated Line lines = 2; } message FunctionCall { - string name = 1; - repeated string arguments = 2; + TextWithArgs name = 1; } message Command { - string text = 1; - repeated Command children = 2; + TextWithArgs text = 1; } From 659df1ef5e7b76bd830de2d76bf602d6135ce302 Mon Sep 17 00:00:00 2001 From: Benjamin Bolte Date: Wed, 30 Oct 2024 23:00:07 -0700 Subject: [PATCH 3/7] some stuff --- examples/simple.k | 10 +++++----- klang/src/parser/lang.rs | 3 ++- klang/src/parser/mod.rs | 17 ++--------------- klang/src/parser/structs.rs | 6 ++++-- 4 files changed, 13 insertions(+), 23 deletions(-) diff --git a/examples/simple.k b/examples/simple.k index f04b9cb..57eae5b 100644 --- a/examples/simple.k +++ b/examples/simple.k @@ -1,12 +1,12 @@ > wave [arm] arm { > wave [arm] joint [joint] twice { - move [arm] joint [joint] to 90 - move [arm] joint [joint] to 0 + move joint [joint] on [arm] to 90 + move joint [joint] on [arm] to 0 } - wave [arm] joint 1 twice - wave [arm] joint 2 twice - wave [arm] joint 3 twice + " wave [arm] joint [1] twice + " wave [arm] joint [2] twice + " wave [arm] joint [3] twice } > wave both arms { diff --git a/klang/src/parser/lang.rs b/klang/src/parser/lang.rs index d14d904..a1c6139 100644 --- a/klang/src/parser/lang.rs +++ b/klang/src/parser/lang.rs @@ -31,7 +31,8 @@ pub fn parse_program(pair: pest::iterators::Pair) -> Result Result<(), ParseError> { if binary { let mut buf = Vec::new(); - prost::Message::encode(&program.program, &mut buf).map_err(|e| { + prost::Message::encode(&program.ast_program, &mut buf).map_err(|e| { ParseError::new(format!( "Error encoding program to file '{}': {}", file_path.display(), @@ -60,7 +60,7 @@ pub fn write_program_to_file( )) })?; } else { - let program_str = format!("{:#?}", program.program); + let program_str = format!("{:#?}", program.ast_program); fs::write(file_path, &program_str).map_err(|e| { ParseError::new(format!( "Error writing program to file '{}': {}", @@ -72,16 +72,3 @@ pub fn write_program_to_file( Ok(()) } - -pub fn read_program_from_file(file_path: &Path) -> Result { - let buf = fs::read(file_path).map_err(|e| { - ParseError::new(format!( - "Error reading file '{}': {}", - file_path.display(), - e - )) - })?; - let program = prost::Message::decode(&*buf) - .map_err(|e| ParseError::new(format!("Error decoding program: {}", e)))?; - Ok(KlangProgram { program }) -} diff --git a/klang/src/parser/structs.rs b/klang/src/parser/structs.rs index 46c63ef..f5f01ff 100644 --- a/klang/src/parser/structs.rs +++ b/klang/src/parser/structs.rs @@ -1,4 +1,5 @@ -use super::ast::Program; +use super::ast::Program as AstProgram; +use super::ir::Program as IrProgram; use pest_derive::Parser; #[derive(Parser)] @@ -6,5 +7,6 @@ use pest_derive::Parser; pub struct PestParser; pub struct KlangProgram { - pub program: Program, + pub ast_program: AstProgram, + pub ir_program: IrProgram, } From 6730c0587f5ecd7dda9ebd1d89bc41146dd0a103 Mon Sep 17 00:00:00 2001 From: Benjamin Bolte Date: Wed, 30 Oct 2024 23:05:54 -0700 Subject: [PATCH 4/7] things kind of work --- examples/simple.k | 12 ++++++------ klang/src/parser/passes/mod.rs | 24 +++++++++++++++++++++--- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/examples/simple.k b/examples/simple.k index 57eae5b..cee409b 100644 --- a/examples/simple.k +++ b/examples/simple.k @@ -1,12 +1,12 @@ > wave [arm] arm { - > wave [arm] joint [joint] twice { - move joint [joint] on [arm] to 90 - move joint [joint] on [arm] to 0 + > wave joint [joint] twice { + move joint [joint] on the [arm] arm to 90 + move joint [joint] on the [arm] arm to 0 } - " wave [arm] joint [1] twice - " wave [arm] joint [2] twice - " wave [arm] joint [3] twice + " wave joint [1] twice + " wave joint [2] twice + " wave joint [3] twice } > wave both arms { diff --git a/klang/src/parser/passes/mod.rs b/klang/src/parser/passes/mod.rs index ae1d904..2619062 100644 --- a/klang/src/parser/passes/mod.rs +++ b/klang/src/parser/passes/mod.rs @@ -34,6 +34,7 @@ fn get_function_signature(name: &TextWithArgs) -> (String, Vec) { fn match_function_call( call_name: &TextWithArgs, func_signature: &TextWithArgs, + current_arg_map: &HashMap, ) -> Option> { let mut args = HashMap::new(); let mut call_iter = call_name.parts.iter(); @@ -55,7 +56,11 @@ fn match_function_call( Some(PartKind::FunctionArg(call_arg)), Some(PartKind::FunctionArg(sig_arg)), ) => { - args.insert(sig_arg.text.clone(), call_arg.text.clone()); + if let Some(value) = current_arg_map.get(&call_arg.text) { + args.insert(sig_arg.text.clone(), value.clone()); + } else { + args.insert(sig_arg.text.clone(), call_arg.text.clone()); + } } _ => { return None; @@ -83,7 +88,17 @@ fn process_line_with_args( let (call_signature, _) = get_function_signature(name); for (func_sig, (func_def, _)) in functions { if let Some(name_def) = &func_def.name { - if let Some(arg_map) = match_function_call(name, name_def) { + if let Some(mut new_arg_map) = + match_function_call(name, name_def, arg_map) + { + // Merge parent scope arguments with new arguments + // New arguments take precedence over parent scope + for (key, value) in arg_map.iter() { + new_arg_map + .entry(key.clone()) + .or_insert_with(|| value.clone()); + } + if call_stack.contains(func_sig) { return Err(ParseError::new(format!( "Recursive function call: {}", @@ -94,7 +109,10 @@ fn process_line_with_args( let mut commands = Vec::new(); for inner_line in &func_def.lines { let mut cmds = process_line_with_args( - inner_line, functions, call_stack, &arg_map, + inner_line, + functions, + call_stack, + &new_arg_map, )?; commands.append(&mut cmds); } From a7c60c32bd2708f6f0867e5f502e91b72fa3b671 Mon Sep 17 00:00:00 2001 From: Benjamin Bolte Date: Wed, 30 Oct 2024 23:27:36 -0700 Subject: [PATCH 5/7] v0.0.1 --- klang/src/parser/mod.rs | 23 +++++++++++++++++++++-- klang/src/parser/passes/mod.rs | 21 ++++++++++++++++----- 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/klang/src/parser/mod.rs b/klang/src/parser/mod.rs index 9ea9e63..000b8ee 100644 --- a/klang/src/parser/mod.rs +++ b/klang/src/parser/mod.rs @@ -60,8 +60,27 @@ pub fn write_program_to_file( )) })?; } else { - let program_str = format!("{:#?}", program.ast_program); - fs::write(file_path, &program_str).map_err(|e| { + let mut output = String::new(); + + fn render_command(cmd: &ast::Command, indent: usize) -> String { + let mut result = format!("{:indent$}{}", "", cmd.text, indent = indent); + if !cmd.children.is_empty() { + result.push_str(" {\n"); + for child in &cmd.children { + result.push_str(&render_command(child, indent + 2)); + } + result.push_str(&format!("{:indent$}}}\n", "", indent = indent)); + } else { + result.push('\n'); + } + result + } + + for command in &program.ast_program.commands { + output.push_str(&render_command(command, 0)); + } + + fs::write(file_path, &output).map_err(|e| { ParseError::new(format!( "Error writing program to file '{}': {}", file_path.display(), diff --git a/klang/src/parser/passes/mod.rs b/klang/src/parser/passes/mod.rs index 2619062..8e1d06f 100644 --- a/klang/src/parser/passes/mod.rs +++ b/klang/src/parser/passes/mod.rs @@ -106,7 +106,12 @@ fn process_line_with_args( ))); } call_stack.push(func_sig.clone()); - let mut commands = Vec::new(); + + // Create parent command with function name + let function_text = substitute_text_with_args(name, arg_map)?; + + // Process child commands + let mut children = Vec::new(); for inner_line in &func_def.lines { let mut cmds = process_line_with_args( inner_line, @@ -114,10 +119,16 @@ fn process_line_with_args( call_stack, &new_arg_map, )?; - commands.append(&mut cmds); + children.append(&mut cmds); } + call_stack.pop(); - return Ok(commands); + + // Return single command with children + return Ok(vec![AstCommand { + text: function_text, + children, + }]); } } } @@ -152,7 +163,7 @@ fn process_command_with_args( let text = substitute_text_with_args(text, arg_map)?; Ok(AstCommand { text, - children: Vec::new(), // Removed children handling since field doesn't exist + children: Vec::new(), }) } else { Err(ParseError::new("Command without text".to_string())) @@ -179,7 +190,7 @@ fn substitute_text_with_args( if let Some(value) = arg_map.get(&arg.text) { result.push_str(value); } else { - return Err(ParseError::new(format!("Argument not found: {}", arg.text))); + result.push_str(&arg.text); } } None => {} From 161b91c9a9bf14da89142ef11a3bc25a8d4b84bb Mon Sep 17 00:00:00 2001 From: Benjamin Bolte Date: Wed, 30 Oct 2024 23:30:42 -0700 Subject: [PATCH 6/7] format --- klang/src/parser/lang.rs | 2 +- klang/src/parser/passes/mod.rs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/klang/src/parser/lang.rs b/klang/src/parser/lang.rs index a1c6139..be28cb4 100644 --- a/klang/src/parser/lang.rs +++ b/klang/src/parser/lang.rs @@ -63,7 +63,7 @@ fn parse_line(line: Pair) -> Result, ParseError> { line_pair, ))), }) - .filter_map(|line| line) + .flatten() .collect() } diff --git a/klang/src/parser/passes/mod.rs b/klang/src/parser/passes/mod.rs index 8e1d06f..ab40788 100644 --- a/klang/src/parser/passes/mod.rs +++ b/klang/src/parser/passes/mod.rs @@ -13,16 +13,16 @@ fn get_function_signature(name: &TextWithArgs) -> (String, Vec) { if first { first = false; } else { - signature.push_str(" "); + signature.push(' '); } match &part.part_kind { Some(PartKind::Text(text)) => { signature.push_str(text); } Some(PartKind::FunctionArg(arg)) => { - signature.push_str("["); + signature.push('['); signature.push_str(&arg.text); - signature.push_str("]"); + signature.push(']'); params.push(arg.text.clone()); } None => {} @@ -180,7 +180,7 @@ fn substitute_text_with_args( if first { first = false; } else { - result.push_str(" "); + result.push(' '); } match &part.part_kind { Some(PartKind::Text(text)) => { From d8d9793d8d1b6d9ecc5e5009da1d58f885cfb9dd Mon Sep 17 00:00:00 2001 From: Benjamin Bolte Date: Wed, 30 Oct 2024 23:32:45 -0700 Subject: [PATCH 7/7] lint again --- klang/src/parser/lang.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/klang/src/parser/lang.rs b/klang/src/parser/lang.rs index be28cb4..18d8144 100644 --- a/klang/src/parser/lang.rs +++ b/klang/src/parser/lang.rs @@ -38,7 +38,7 @@ pub fn parse_program(pair: pest::iterators::Pair) -> Result) -> Result, ParseError> { line.into_inner() - .map(|line_pair| match line_pair.as_rule() { + .filter_map(|line_pair| match line_pair.as_rule() { Rule::function_def => match parse_function_def(line_pair) { Ok(func) => Some(Ok(Line { line_kind: Some(LineKind::Function(func)), @@ -63,7 +63,6 @@ fn parse_line(line: Pair) -> Result, ParseError> { line_pair, ))), }) - .flatten() .collect() }