Working full parser
This commit is contained in:
@@ -112,6 +112,7 @@ pub enum Expression {
|
|||||||
Call(Box<CallExpr>),
|
Call(Box<CallExpr>),
|
||||||
FieldAccess(Box<FieldAccess>),
|
FieldAccess(Box<FieldAccess>),
|
||||||
StructInit(Box<StructInit>),
|
StructInit(Box<StructInit>),
|
||||||
|
ArrayLiteral(Box<ArrayLiteral>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -178,3 +179,9 @@ pub enum TypeExpr {
|
|||||||
Array(Box<TypeExpr>),
|
Array(Box<TypeExpr>),
|
||||||
Optional(Box<TypeExpr>),
|
Optional(Box<TypeExpr>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ArrayLiteral {
|
||||||
|
pub elements: Vec<Expression>,
|
||||||
|
pub span: Span,
|
||||||
|
}
|
||||||
|
|||||||
+37
-8
@@ -23,21 +23,50 @@ param = { identifier ~ ":" ~ type_expr }
|
|||||||
|
|
||||||
// statements
|
// statements
|
||||||
block = { "{" ~ statement* ~ "}" }
|
block = { "{" ~ statement* ~ "}" }
|
||||||
statement = _{ let_stmt | return_stmt | if_stmt | for_stmt | expression }
|
statement = _{ let_stmt | return_stmt | if_stmt | for_stmt | assign_stmt | expression }
|
||||||
|
|
||||||
|
assign_stmt = { identifier ~ "=" ~ expression }
|
||||||
let_stmt = { "let" ~ identifier ~ (":" ~ type_expr)? ~ "=" ~ expression }
|
let_stmt = { "let" ~ identifier ~ (":" ~ type_expr)? ~ "=" ~ expression }
|
||||||
return_stmt = { "return" ~ expression? }
|
return_stmt = { "return" ~ expression? }
|
||||||
if_stmt = { "if" ~ expression ~ block ~ ("else" ~ block)? }
|
if_stmt = { "if" ~ expression ~ block ~ ("else" ~ block)? }
|
||||||
for_stmt = { "for" ~ identifier ~ "in" ~ expression ~ block }
|
for_stmt = { "for" ~ identifier ~ "in" ~ expression ~ block }
|
||||||
|
|
||||||
// expressions — broken into precedence layers, no cycles
|
|
||||||
expression = { primary ~ (access_chain)* }
|
|
||||||
access_chain = _{ field_access | call_suffix }
|
|
||||||
field_access = { "." ~ identifier }
|
|
||||||
call_suffix = { "(" ~ (expression ~ ("," ~ expression)*)? ~ ")" }
|
|
||||||
primary = _{ float | integer | string_lit | boolean | identifier }
|
|
||||||
|
|
||||||
// types — optional is a suffix, not a recursive wrapper
|
// types — optional is a suffix, not a recursive wrapper
|
||||||
type_expr = { base_type ~ "?"? }
|
type_expr = { base_type ~ "?"? }
|
||||||
base_type = _{ array_type | identifier }
|
base_type = _{ array_type | identifier }
|
||||||
array_type = { "[" ~ type_expr ~ "]" }
|
array_type = { "[" ~ type_expr ~ "]" }
|
||||||
|
|
||||||
|
// expressions
|
||||||
|
expression = { term ~ (bin_op ~ term)* }
|
||||||
|
bin_op = _{ add | sub | mul | div | eq | neq | lte | gte | lt | gt | and | or }
|
||||||
|
add = { "+" }
|
||||||
|
sub = { "-" }
|
||||||
|
mul = { "*" }
|
||||||
|
div = { "/" }
|
||||||
|
eq = { "==" }
|
||||||
|
neq = { "!=" }
|
||||||
|
lte = { "<=" }
|
||||||
|
gte = { ">=" }
|
||||||
|
lt = { "<" }
|
||||||
|
gt = { ">" }
|
||||||
|
and = { "&&" }
|
||||||
|
or = { "||" }
|
||||||
|
|
||||||
|
struct_literal = {
|
||||||
|
identifier ~ "{" ~ (struct_init_field ~ ("," ~ struct_init_field)*)? ~ "}"
|
||||||
|
}
|
||||||
|
|
||||||
|
struct_init_field = {
|
||||||
|
identifier ~ ":" ~ expression
|
||||||
|
}
|
||||||
|
|
||||||
|
array_literal = {
|
||||||
|
"[" ~ (expression ~ ("," ~ expression)*)? ~ "]"
|
||||||
|
}
|
||||||
|
|
||||||
|
term = { primary ~ (field_access | call_suffix)* }
|
||||||
|
field_access = { "." ~ identifier }
|
||||||
|
call_suffix = { "(" ~ (expression ~ ("," ~ expression)*)? ~ ")" }
|
||||||
|
primary = _{ struct_literal | array_literal | float | integer | string_lit | boolean | self_kw | null_kw | identifier }
|
||||||
|
self_kw = { "self" }
|
||||||
|
null_kw = { "null" }
|
||||||
|
|||||||
+87
-25
@@ -219,52 +219,114 @@ fn parse_for(pair: Pair<Rule>) -> ForStatement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn parse_expression(pair: Pair<Rule>) -> Expression {
|
fn parse_expression(pair: Pair<Rule>) -> Expression {
|
||||||
// let span = span_of(&pair);
|
|
||||||
match pair.as_rule() {
|
match pair.as_rule() {
|
||||||
Rule::expression => {
|
Rule::expression => {
|
||||||
let mut inner = pair.into_inner();
|
let mut inner = pair.into_inner();
|
||||||
let mut expr = parse_primary(inner.next().unwrap());
|
let mut expr = parse_term(inner.next().unwrap());
|
||||||
|
|
||||||
// chain field accesses and calls as left-to-right suffixes
|
// consume pairs of (bin_op, term)
|
||||||
for part in inner {
|
while let Some(op_pair) = inner.next() {
|
||||||
let span = span_of(&part);
|
let right = parse_term(inner.next().unwrap());
|
||||||
match part.as_rule() {
|
let span = span_of(&op_pair);
|
||||||
Rule::field_access => {
|
let op = match op_pair.as_rule() {
|
||||||
let field = part.into_inner().next().unwrap().as_str().to_string();
|
Rule::add => BinOperator::Add,
|
||||||
expr = Expression::FieldAccess(Box::new(FieldAccess {
|
Rule::sub => BinOperator::Sub,
|
||||||
object: expr,
|
Rule::mul => BinOperator::Mul,
|
||||||
field,
|
Rule::div => BinOperator::Div,
|
||||||
span,
|
Rule::eq => BinOperator::Eq,
|
||||||
}));
|
Rule::neq => BinOperator::NotEq,
|
||||||
}
|
Rule::lt => BinOperator::Lt,
|
||||||
Rule::call_suffix => {
|
Rule::gt => BinOperator::Gt,
|
||||||
let args = part.into_inner().map(|p| parse_expression(p)).collect();
|
Rule::lte => BinOperator::LtEq,
|
||||||
expr = Expression::Call(Box::new(CallExpr {
|
Rule::gte => BinOperator::GtEq,
|
||||||
callee: expr,
|
Rule::and => BinOperator::And,
|
||||||
args,
|
Rule::or => BinOperator::Or,
|
||||||
span,
|
_ => unreachable!(),
|
||||||
}));
|
};
|
||||||
}
|
expr = Expression::BinaryOp(Box::new(BinaryOp {
|
||||||
_ => {}
|
left: expr,
|
||||||
}
|
op,
|
||||||
|
right,
|
||||||
|
span,
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
expr
|
expr
|
||||||
}
|
}
|
||||||
_ => parse_primary(pair),
|
_ => parse_term(pair),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_term(pair: Pair<Rule>) -> Expression {
|
||||||
|
let mut inner = pair.into_inner();
|
||||||
|
let mut expr = parse_primary(inner.next().unwrap());
|
||||||
|
|
||||||
|
for part in inner {
|
||||||
|
let span = span_of(&part);
|
||||||
|
match part.as_rule() {
|
||||||
|
Rule::field_access => {
|
||||||
|
let field = part.into_inner().next().unwrap().as_str().to_string();
|
||||||
|
expr = Expression::FieldAccess(Box::new(FieldAccess {
|
||||||
|
object: expr,
|
||||||
|
field,
|
||||||
|
span,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
Rule::call_suffix => {
|
||||||
|
let args = part.into_inner().map(|p| parse_expression(p)).collect();
|
||||||
|
expr = Expression::Call(Box::new(CallExpr {
|
||||||
|
callee: expr,
|
||||||
|
args,
|
||||||
|
span,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expr
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_primary(pair: Pair<Rule>) -> Expression {
|
fn parse_primary(pair: Pair<Rule>) -> Expression {
|
||||||
let span = span_of(&pair);
|
let span = span_of(&pair);
|
||||||
|
|
||||||
match pair.as_rule() {
|
match pair.as_rule() {
|
||||||
|
Rule::struct_literal => {
|
||||||
|
let mut inner = pair.into_inner();
|
||||||
|
let name = inner.next().unwrap().as_str().to_string();
|
||||||
|
|
||||||
|
let mut fields = vec![];
|
||||||
|
for field in inner {
|
||||||
|
let mut f_inner = field.into_inner();
|
||||||
|
let field_name = f_inner.next().unwrap().as_str().to_string();
|
||||||
|
let value = parse_expression(f_inner.next().unwrap());
|
||||||
|
fields.push((field_name, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
Expression::StructInit(Box::new(StructInit { name, fields, span }))
|
||||||
|
}
|
||||||
|
|
||||||
|
Rule::array_literal => {
|
||||||
|
let elements = pair.into_inner().map(|p| parse_expression(p)).collect();
|
||||||
|
|
||||||
|
Expression::ArrayLiteral(Box::new(ArrayLiteral { elements, span }))
|
||||||
|
}
|
||||||
|
|
||||||
Rule::integer => Expression::Integer(pair.as_str().parse().unwrap(), span),
|
Rule::integer => Expression::Integer(pair.as_str().parse().unwrap(), span),
|
||||||
Rule::float => Expression::Float(pair.as_str().parse().unwrap(), span),
|
Rule::float => Expression::Float(pair.as_str().parse().unwrap(), span),
|
||||||
|
|
||||||
Rule::string_lit => {
|
Rule::string_lit => {
|
||||||
Expression::StringLit(pair.into_inner().next().unwrap().as_str().to_string(), span)
|
Expression::StringLit(pair.into_inner().next().unwrap().as_str().to_string(), span)
|
||||||
}
|
}
|
||||||
|
|
||||||
Rule::boolean => Expression::Bool(pair.as_str() == "true", span),
|
Rule::boolean => Expression::Bool(pair.as_str() == "true", span),
|
||||||
|
|
||||||
|
Rule::self_kw => Expression::Identifier("self".to_string(), span),
|
||||||
|
Rule::null_kw => Expression::Identifier("null".to_string(), span),
|
||||||
Rule::identifier => Expression::Identifier(pair.as_str().to_string(), span),
|
Rule::identifier => Expression::Identifier(pair.as_str().to_string(), span),
|
||||||
|
|
||||||
|
Rule::term => parse_term(pair),
|
||||||
|
|
||||||
_ => unreachable!("unexpected primary rule: {:?}", pair.as_rule()),
|
_ => unreachable!("unexpected primary rule: {:?}", pair.as_rule()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+117
-1
@@ -1,4 +1,120 @@
|
|||||||
|
import "fmt"
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
// structs are plain data
|
||||||
|
struct Point {
|
||||||
|
x: float
|
||||||
|
y: float
|
||||||
|
}
|
||||||
|
|
||||||
|
// classes have fields and methods
|
||||||
|
class Animal {
|
||||||
|
name: string
|
||||||
|
age: int
|
||||||
|
|
||||||
|
fn speak() -> string {
|
||||||
|
return "..."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn describe() -> string {
|
||||||
|
return "I am " + self.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Dog {
|
||||||
|
name: string
|
||||||
|
age: int
|
||||||
|
|
||||||
|
fn speak() -> string {
|
||||||
|
return "Woof!"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fetch(item: string) -> string {
|
||||||
|
return self.name + " fetched the " + item
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// basic function
|
||||||
|
fn add(a: int, b: int) -> int {
|
||||||
|
return a + b
|
||||||
|
}
|
||||||
|
|
||||||
|
// optional return type
|
||||||
|
fn find(name: string) -> string? {
|
||||||
|
if name == "mist" {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
// array types
|
||||||
|
fn sum(numbers: [int]) -> int {
|
||||||
|
let total: int = 0
|
||||||
|
for n in numbers {
|
||||||
|
total = total + n
|
||||||
|
}
|
||||||
|
return total
|
||||||
|
}
|
||||||
|
|
||||||
|
// multiple params, no return
|
||||||
|
fn greet(first: string, last: string) {
|
||||||
|
let full: string = first + " " + last
|
||||||
|
print(full)
|
||||||
|
}
|
||||||
|
|
||||||
|
// structs initialized with fields
|
||||||
|
fn make_point(x: float, y: float) -> Point {
|
||||||
|
return Point {
|
||||||
|
x: x,
|
||||||
|
y: y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if / else
|
||||||
|
fn classify(n: int) -> string {
|
||||||
|
if n < 0 {
|
||||||
|
return "negative"
|
||||||
|
} else {
|
||||||
|
return "positive"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// entry point
|
||||||
fn main() {
|
fn main() {
|
||||||
let message: string = "Hello, World!"
|
// variables
|
||||||
|
let x: int = 10
|
||||||
|
let y: int = 20
|
||||||
|
let result: int = add(x, y)
|
||||||
|
|
||||||
|
// inferred (no type annotation)
|
||||||
|
let message = "Hello from Mist!"
|
||||||
print(message)
|
print(message)
|
||||||
|
|
||||||
|
// struct usage
|
||||||
|
let p: Point = make_point(1.5, 2.5)
|
||||||
|
print(p.x)
|
||||||
|
|
||||||
|
// class usage
|
||||||
|
let dog: Dog = Dog {
|
||||||
|
name: "Rex",
|
||||||
|
age: 3
|
||||||
|
}
|
||||||
|
print(dog.speak())
|
||||||
|
print(dog.fetch("ball"))
|
||||||
|
|
||||||
|
// arrays
|
||||||
|
let nums: [int] = [1, 2, 3, 4, 5]
|
||||||
|
let total: int = sum(nums)
|
||||||
|
print(total)
|
||||||
|
|
||||||
|
// for loop
|
||||||
|
for n in nums {
|
||||||
|
print(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
// optional
|
||||||
|
let found: string? = find("mist")
|
||||||
|
if found {
|
||||||
|
print(found)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user