use super::ast::{
BinaryOp, ContractNode, Expression, MethodNode, ParamNode, PrimitiveTypeName, PropertyNode,
SourceLocation, Statement, TypeNode, UnaryOp, Visibility,
};
use super::diagnostic::Diagnostic;
use super::parser::ParseResult;
pub fn parse_go_contract(source: &str, file_name: Option<&str>) -> ParseResult {
let file = file_name.unwrap_or("contract.runar.go");
let mut errors: Vec<Diagnostic> = Vec::new();
let tokens = tokenize(source);
let mut parser = GoParser::new(tokens, file, &mut errors);
let contract = parser.parse();
ParseResult { contract, errors }
}
fn go_to_camel(name: &str) -> String {
if name.is_empty() {
return name.to_string();
}
let mut chars = name.chars();
let first = chars.next().unwrap();
if first.is_uppercase() {
let rest: String = chars.collect();
format!("{}{}", first.to_lowercase(), rest)
} else {
name.to_string()
}
}
fn map_go_type(name: &str) -> TypeNode {
match name {
"Int" | "Bigint" | "int64" | "uint64" | "int" | "uint" => {
TypeNode::Primitive(PrimitiveTypeName::Bigint)
}
"Bool" | "bool" => TypeNode::Primitive(PrimitiveTypeName::Boolean),
"ByteString" | "[]byte" | "string" => TypeNode::Primitive(PrimitiveTypeName::ByteString),
"PubKey" => TypeNode::Primitive(PrimitiveTypeName::PubKey),
"Sig" => TypeNode::Primitive(PrimitiveTypeName::Sig),
"Sha256" => TypeNode::Primitive(PrimitiveTypeName::Sha256),
"Ripemd160" => TypeNode::Primitive(PrimitiveTypeName::Ripemd160),
"Addr" => TypeNode::Primitive(PrimitiveTypeName::Addr),
"SigHashPreimage" => TypeNode::Primitive(PrimitiveTypeName::SigHashPreimage),
"RabinSig" => TypeNode::Primitive(PrimitiveTypeName::RabinSig),
"RabinPubKey" => TypeNode::Primitive(PrimitiveTypeName::RabinPubKey),
"Point" => TypeNode::Primitive(PrimitiveTypeName::Point),
_ => TypeNode::Custom(name.to_string()),
}
}
fn map_go_builtin(name: &str) -> String {
match name {
"Assert" => "assert".to_string(),
"Hash160" => "hash160".to_string(),
"Hash256" => "hash256".to_string(),
"Sha256" => "sha256".to_string(),
"Ripemd160" => "ripemd160".to_string(),
"CheckSig" => "checkSig".to_string(),
"CheckMultiSig" => "checkMultiSig".to_string(),
"CheckPreimage" => "checkPreimage".to_string(),
"VerifyRabinSig" => "verifyRabinSig".to_string(),
"VerifyWOTS" => "verifyWOTS".to_string(),
"VerifySLHDSA_SHA2_128s" => "verifySLHDSA_SHA2_128s".to_string(),
"VerifySLHDSA_SHA2_128f" => "verifySLHDSA_SHA2_128f".to_string(),
"VerifySLHDSA_SHA2_192s" => "verifySLHDSA_SHA2_192s".to_string(),
"VerifySLHDSA_SHA2_192f" => "verifySLHDSA_SHA2_192f".to_string(),
"VerifySLHDSA_SHA2_256s" => "verifySLHDSA_SHA2_256s".to_string(),
"VerifySLHDSA_SHA2_256f" => "verifySLHDSA_SHA2_256f".to_string(),
"Num2Bin" => "num2bin".to_string(),
"Bin2Num" => "bin2num".to_string(),
"Cat" => "cat".to_string(),
"Substr" => "substr".to_string(),
"Len" => "len".to_string(),
"ReverseBytes" => "reverseBytes".to_string(),
"ExtractLocktime" => "extractLocktime".to_string(),
"ExtractOutputHash" => "extractOutputHash".to_string(),
"AddOutput" => "addOutput".to_string(),
"GetStateScript" => "getStateScript".to_string(),
"Safediv" => "safediv".to_string(),
"Safemod" => "safemod".to_string(),
"Clamp" => "clamp".to_string(),
"Sign" => "sign".to_string(),
"Pow" => "pow".to_string(),
"MulDiv" => "mulDiv".to_string(),
"PercentOf" => "percentOf".to_string(),
"Sqrt" => "sqrt".to_string(),
"Gcd" => "gcd".to_string(),
"Divmod" => "divmod".to_string(),
"Log2" => "log2".to_string(),
"ToBool" => "bool".to_string(),
"Abs" => "abs".to_string(),
"Min" => "min".to_string(),
"Max" => "max".to_string(),
"Within" => "within".to_string(),
"EcAdd" => "ecAdd".to_string(),
"EcMul" => "ecMul".to_string(),
"EcMulGen" => "ecMulGen".to_string(),
"EcNegate" => "ecNegate".to_string(),
"EcOnCurve" => "ecOnCurve".to_string(),
"EcModReduce" => "ecModReduce".to_string(),
"EcEncodeCompressed" => "ecEncodeCompressed".to_string(),
"EcMakePoint" => "ecMakePoint".to_string(),
"EcPointX" => "ecPointX".to_string(),
"EcPointY" => "ecPointY".to_string(),
"Sha256Compress" => "sha256Compress".to_string(),
"Sha256Finalize" => "sha256Finalize".to_string(),
_ => go_to_camel(name),
}
}
#[derive(Debug, Clone, PartialEq)]
enum TokenType {
Package,
Import,
Type,
Struct,
Func,
If,
Else,
For,
Return,
Var,
Const,
True,
False,
LParen,
RParen,
LBrace,
RBrace,
LBracket,
RBracket,
Semi,
Comma,
Dot,
Colon,
Star,
Plus,
Minus,
Slash,
Percent,
EqEq, BangEq, Lt,
LtEq,
Gt,
GtEq,
AmpAmp, PipePipe, Amp,
Pipe,
Caret,
Tilde,
Bang,
Eq, ColonEq, PlusEq, MinusEq, PlusPlus, MinusMinus, Ident(String),
Number(i64),
StringLit(String), Eof,
}
#[derive(Debug, Clone)]
struct Token {
typ: TokenType,
line: usize,
col: usize,
}
fn tokenize(source: &str) -> Vec<Token> {
let chars: Vec<char> = source.chars().collect();
let mut tokens = Vec::new();
let mut pos = 0usize;
let mut line = 1usize;
let mut col = 1usize;
while pos < chars.len() {
let ch = chars[pos];
let l = line;
let c = col;
if ch.is_whitespace() {
if ch == '\n' {
line += 1;
col = 1;
} else {
col += 1;
}
pos += 1;
continue;
}
if ch == '/' && pos + 1 < chars.len() && chars[pos + 1] == '/' {
while pos < chars.len() && chars[pos] != '\n' {
pos += 1;
}
continue;
}
if ch == '/' && pos + 1 < chars.len() && chars[pos + 1] == '*' {
pos += 2;
col += 2;
while pos + 1 < chars.len() {
if chars[pos] == '\n' {
line += 1;
col = 1;
}
if chars[pos] == '*' && chars[pos + 1] == '/' {
pos += 2;
col += 2;
break;
}
pos += 1;
col += 1;
}
continue;
}
if ch == '`' {
let mut val = String::new();
pos += 1;
col += 1;
while pos < chars.len() && chars[pos] != '`' {
val.push(chars[pos]);
pos += 1;
col += 1;
}
if pos < chars.len() {
pos += 1;
col += 1;
}
tokens.push(Token { typ: TokenType::StringLit(val), line: l, col: c });
continue;
}
if ch == '"' {
let mut val = String::new();
pos += 1;
col += 1;
while pos < chars.len() && chars[pos] != '"' {
if chars[pos] == '\\' && pos + 1 < chars.len() {
pos += 1;
col += 1;
match chars[pos] {
'n' => val.push('\n'),
't' => val.push('\t'),
'\\' => val.push('\\'),
'"' => val.push('"'),
c => { val.push('\\'); val.push(c); }
}
} else {
val.push(chars[pos]);
}
pos += 1;
col += 1;
}
if pos < chars.len() {
pos += 1;
col += 1;
}
tokens.push(Token { typ: TokenType::StringLit(val), line: l, col: c });
continue;
}
if pos + 2 < chars.len() {
let three = format!("{}{}{}", ch, chars[pos + 1], chars[pos + 2]);
let _ = three;
}
if pos + 1 < chars.len() {
let two = (ch, chars[pos + 1]);
let tok = match two {
(':', '=') => Some(TokenType::ColonEq),
('=', '=') => Some(TokenType::EqEq),
('!', '=') => Some(TokenType::BangEq),
('<', '=') => Some(TokenType::LtEq),
('>', '=') => Some(TokenType::GtEq),
('&', '&') => Some(TokenType::AmpAmp),
('|', '|') => Some(TokenType::PipePipe),
('+', '=') => Some(TokenType::PlusEq),
('-', '=') => Some(TokenType::MinusEq),
('+', '+') => Some(TokenType::PlusPlus),
('-', '-') => Some(TokenType::MinusMinus),
_ => None,
};
if let Some(t) = tok {
tokens.push(Token { typ: t, line: l, col: c });
pos += 2;
col += 2;
continue;
}
}
let single = match ch {
'(' => Some(TokenType::LParen),
')' => Some(TokenType::RParen),
'{' => Some(TokenType::LBrace),
'}' => Some(TokenType::RBrace),
'[' => Some(TokenType::LBracket),
']' => Some(TokenType::RBracket),
';' => Some(TokenType::Semi),
',' => Some(TokenType::Comma),
'.' => Some(TokenType::Dot),
':' => Some(TokenType::Colon),
'*' => Some(TokenType::Star),
'+' => Some(TokenType::Plus),
'-' => Some(TokenType::Minus),
'/' => Some(TokenType::Slash),
'%' => Some(TokenType::Percent),
'<' => Some(TokenType::Lt),
'>' => Some(TokenType::Gt),
'&' => Some(TokenType::Amp),
'|' => Some(TokenType::Pipe),
'^' => Some(TokenType::Caret),
'~' => Some(TokenType::Tilde),
'!' => Some(TokenType::Bang),
'=' => Some(TokenType::Eq),
_ => None,
};
if let Some(t) = single {
tokens.push(Token { typ: t, line: l, col: c });
pos += 1;
col += 1;
continue;
}
if ch.is_ascii_digit() {
let mut val = String::new();
while pos < chars.len() && (chars[pos].is_ascii_digit() || chars[pos] == '_') {
if chars[pos] != '_' {
val.push(chars[pos]);
}
pos += 1;
col += 1;
}
let n: i64 = val.parse().unwrap_or(0);
tokens.push(Token { typ: TokenType::Number(n), line: l, col: c });
continue;
}
if ch.is_alphabetic() || ch == '_' {
let mut val = String::new();
while pos < chars.len() && (chars[pos].is_alphanumeric() || chars[pos] == '_') {
val.push(chars[pos]);
pos += 1;
col += 1;
}
let tok = match val.as_str() {
"package" => TokenType::Package,
"import" => TokenType::Import,
"type" => TokenType::Type,
"struct" => TokenType::Struct,
"func" => TokenType::Func,
"if" => TokenType::If,
"else" => TokenType::Else,
"for" => TokenType::For,
"return" => TokenType::Return,
"var" => TokenType::Var,
"const" => TokenType::Const,
"true" => TokenType::True,
"false" => TokenType::False,
_ => TokenType::Ident(val),
};
tokens.push(Token { typ: tok, line: l, col: c });
continue;
}
pos += 1;
col += 1;
}
tokens.push(Token { typ: TokenType::Eof, line, col });
tokens
}
struct GoParser<'a> {
tokens: Vec<Token>,
pos: usize,
file: String,
errors: &'a mut Vec<Diagnostic>,
receiver_name: String,
}
impl<'a> GoParser<'a> {
fn new(tokens: Vec<Token>, file: &str, errors: &'a mut Vec<Diagnostic>) -> Self {
Self {
tokens,
pos: 0,
file: file.to_string(),
errors,
receiver_name: "c".to_string(),
}
}
fn current(&self) -> &Token {
self.tokens.get(self.pos).unwrap_or(self.tokens.last().unwrap())
}
fn advance(&mut self) -> Token {
let t = self.current().clone();
if self.pos + 1 < self.tokens.len() {
self.pos += 1;
}
t
}
fn skip_semis(&mut self) {
while matches!(self.current().typ, TokenType::Semi) {
self.advance();
}
}
fn loc(&self) -> SourceLocation {
SourceLocation {
file: self.file.clone(),
line: self.current().line,
column: self.current().col,
}
}
fn loc_at(file: &str, line: usize, col: usize) -> SourceLocation {
SourceLocation { file: file.to_string(), line, column: col }
}
fn expect_ident(&mut self) -> String {
if let TokenType::Ident(name) = self.current().typ.clone() {
self.advance();
name
} else {
self.errors.push(Diagnostic::error(format!(
"Expected identifier, got {:?} at {}:{}",
self.current().typ,
self.current().line,
self.current().col
), None));
String::new()
}
}
fn expect_tok(&mut self, expected: &TokenType) {
if std::mem::discriminant(&self.current().typ) != std::mem::discriminant(expected) {
self.errors.push(Diagnostic::error(format!(
"Expected {:?}, got {:?} at {}:{}:{}",
expected,
self.current().typ,
self.file,
self.current().line,
self.current().col
), None));
}
self.advance();
}
fn match_tok(&mut self, expected: &TokenType) -> bool {
if std::mem::discriminant(&self.current().typ) == std::mem::discriminant(expected) {
self.advance();
true
} else {
false
}
}
fn parse(&mut self) -> Option<ContractNode> {
if matches!(self.current().typ, TokenType::Package) {
self.advance();
self.expect_ident();
self.skip_semis();
}
while matches!(self.current().typ, TokenType::Import) {
self.advance();
self.skip_import_block();
self.skip_semis();
}
let mut contract_name = String::new();
let mut parent_class = "SmartContract".to_string();
let mut properties: Vec<PropertyNode> = Vec::new();
while !matches!(self.current().typ, TokenType::Eof) {
self.skip_semis();
if matches!(self.current().typ, TokenType::Type) {
let saved_pos = self.pos;
self.advance(); if let TokenType::Ident(name) = self.current().typ.clone() {
self.advance(); if matches!(self.current().typ, TokenType::Struct) {
self.advance(); if matches!(self.current().typ, TokenType::LBrace) {
let (found_parent, props) = self.parse_struct_body();
if found_parent.is_some() {
contract_name = name;
parent_class = found_parent.unwrap();
properties = props;
break;
}
}
self.pos = saved_pos;
self.advance(); } else {
self.pos = saved_pos;
self.advance();
}
} else {
self.pos = saved_pos;
self.advance();
}
} else {
self.advance();
}
}
if contract_name.is_empty() {
self.errors.push(Diagnostic::error("no Rúnar contract struct found in Go source", None));
return None;
}
let mut raw_methods: Vec<MethodNode> = Vec::new();
while !matches!(self.current().typ, TokenType::Eof) {
self.skip_semis();
if !matches!(self.current().typ, TokenType::Func) {
self.advance();
continue;
}
self.advance();
if matches!(self.current().typ, TokenType::LParen) {
if let Some(method) = self.parse_method(&contract_name) {
raw_methods.push(method);
}
} else {
if let Some(func) = self.parse_standalone_func() {
raw_methods.push(func);
}
}
}
let mut final_methods: Vec<MethodNode> = Vec::new();
for m in raw_methods {
if m.name == "init" && m.params.is_empty() {
for stmt in &m.body {
if let Statement::Assignment { target, value, .. } = stmt {
if let Expression::PropertyAccess { property } = target {
for prop in properties.iter_mut() {
if &prop.name == property {
prop.initializer = Some(value.clone());
break;
}
}
}
}
}
} else {
final_methods.push(m);
}
}
let uninit_props: Vec<&PropertyNode> =
properties.iter().filter(|p| p.initializer.is_none()).collect();
let constructor_params: Vec<ParamNode> = uninit_props
.iter()
.map(|p| ParamNode { name: p.name.clone(), param_type: p.prop_type.clone() })
.collect();
let super_args: Vec<Expression> = uninit_props
.iter()
.map(|p| Expression::Identifier { name: p.name.clone() })
.collect();
let super_loc = Self::loc_at(&self.file, 1, 1);
let mut constructor_body: Vec<Statement> = Vec::new();
constructor_body.push(Statement::ExpressionStatement {
expression: Expression::CallExpr {
callee: Box::new(Expression::Identifier { name: "super".to_string() }),
args: super_args,
},
source_location: super_loc.clone(),
});
for prop in uninit_props.iter() {
constructor_body.push(Statement::Assignment {
target: Expression::PropertyAccess { property: prop.name.clone() },
value: Expression::Identifier { name: prop.name.clone() },
source_location: super_loc.clone(),
});
}
let constructor = MethodNode {
name: "constructor".to_string(),
params: constructor_params,
body: constructor_body,
visibility: Visibility::Public,
source_location: Self::loc_at(&self.file, 1, 1),
};
Some(ContractNode {
name: contract_name,
parent_class,
properties,
constructor,
methods: final_methods,
source_file: self.file.clone(),
})
}
fn skip_import_block(&mut self) {
if matches!(self.current().typ, TokenType::LParen) {
self.advance();
let mut depth = 1usize;
while !matches!(self.current().typ, TokenType::Eof) {
match self.current().typ {
TokenType::LParen => { depth += 1; self.advance(); }
TokenType::RParen => {
depth -= 1;
self.advance();
if depth == 0 { break; }
}
_ => { self.advance(); }
}
}
} else {
while !matches!(self.current().typ, TokenType::Eof) {
if let TokenType::StringLit(_) = self.current().typ.clone() {
self.advance(); break;
}
if matches!(self.current().typ,
TokenType::Type | TokenType::Func | TokenType::Package) {
break;
}
self.advance();
}
}
}
fn parse_struct_body(&mut self) -> (Option<String>, Vec<PropertyNode>) {
self.expect_tok(&TokenType::LBrace);
let mut parent_class: Option<String> = None;
let mut properties: Vec<PropertyNode> = Vec::new();
while !matches!(self.current().typ, TokenType::RBrace | TokenType::Eof) {
self.skip_semis();
if matches!(self.current().typ, TokenType::RBrace | TokenType::Eof) {
break;
}
let line = self.current().line;
let col_start = self.current().col;
let first_name = if let TokenType::Ident(n) = self.current().typ.clone() {
self.advance();
n
} else {
self.advance();
continue;
};
if matches!(self.current().typ, TokenType::Dot) {
self.advance(); let second = if let TokenType::Ident(n) = self.current().typ.clone() {
self.advance();
n
} else {
continue;
};
if first_name == "runar" {
match second.as_str() {
"SmartContract" => {
parent_class = Some("SmartContract".to_string());
}
"StatefulSmartContract" => {
parent_class = Some("StatefulSmartContract".to_string());
}
_ => {
}
}
if matches!(self.current().typ, TokenType::StringLit(_)) {
self.advance();
}
self.skip_semis();
continue;
}
if matches!(self.current().typ, TokenType::StringLit(_)) {
self.advance();
}
self.skip_semis();
continue;
}
let prop_type = self.parse_type();
let prop_name = go_to_camel(&first_name);
let mut readonly = false;
if let TokenType::StringLit(tag) = self.current().typ.clone() {
self.advance();
if tag.contains(r#"runar:"readonly""#) {
readonly = true;
}
}
let loc = SourceLocation { file: self.file.clone(), line, column: col_start };
properties.push(PropertyNode {
name: prop_name,
prop_type,
readonly,
initializer: None,
source_location: loc,
});
self.skip_semis();
}
self.match_tok(&TokenType::RBrace);
(parent_class, properties)
}
fn parse_type(&mut self) -> TypeNode {
if matches!(self.current().typ, TokenType::LBracket) {
self.advance(); if matches!(self.current().typ, TokenType::RBracket) {
self.advance(); let inner = self.parse_type();
if let TypeNode::Primitive(PrimitiveTypeName::Bigint) = &inner {
}
return TypeNode::Primitive(PrimitiveTypeName::ByteString);
}
let size = if let TokenType::Number(n) = self.current().typ.clone() {
self.advance();
n as usize
} else {
0
};
self.match_tok(&TokenType::RBracket);
let inner = self.parse_type();
return TypeNode::FixedArray { element: Box::new(inner), length: size };
}
if let TokenType::Ident(first) = self.current().typ.clone() {
self.advance();
if matches!(self.current().typ, TokenType::Dot) {
self.advance();
if let TokenType::Ident(second) = self.current().typ.clone() {
self.advance();
if first == "runar" {
return map_go_type(&second);
}
return TypeNode::Custom(format!("{}.{}", first, second));
}
}
return map_go_type(&first);
}
TypeNode::Custom("unknown".to_string())
}
fn parse_method(&mut self, contract_name: &str) -> Option<MethodNode> {
self.expect_tok(&TokenType::LParen);
let recv_name = if let TokenType::Ident(n) = self.current().typ.clone() {
let n_clone = n.clone();
self.advance();
n_clone
} else {
"c".to_string()
};
self.match_tok(&TokenType::Star);
let recv_type = if let TokenType::Ident(n) = self.current().typ.clone() {
self.advance();
n
} else {
String::new()
};
self.expect_tok(&TokenType::RParen);
if recv_type != contract_name {
self.skip_to_matching_brace();
return None;
}
self.receiver_name = recv_name;
let method_name_raw = self.expect_ident();
let method_name = go_to_camel(&method_name_raw);
let visibility = if method_name_raw.chars().next().map(|c| c.is_uppercase()).unwrap_or(false) {
Visibility::Public
} else {
Visibility::Private
};
let loc = SourceLocation {
file: self.file.clone(),
line: self.current().line,
column: self.current().col,
};
let params = self.parse_params();
self.skip_return_type();
let body = self.parse_block();
Some(MethodNode {
name: method_name,
params,
body,
visibility,
source_location: loc,
})
}
fn parse_standalone_func(&mut self) -> Option<MethodNode> {
let func_name_raw = self.expect_ident();
if func_name_raw == "init" || func_name_raw == "main" {
self.skip_to_matching_brace();
return None;
}
let func_name = go_to_camel(&func_name_raw);
let saved_recv = self.receiver_name.clone();
self.receiver_name = String::new();
let loc = SourceLocation {
file: self.file.clone(),
line: self.current().line,
column: self.current().col,
};
let params = self.parse_params();
self.skip_return_type();
let body = self.parse_block();
self.receiver_name = saved_recv;
if func_name_raw.chars().next().map(|c| c.is_uppercase()).unwrap_or(false) {
return None;
}
Some(MethodNode {
name: func_name,
params,
body,
visibility: Visibility::Private,
source_location: loc,
})
}
fn parse_params(&mut self) -> Vec<ParamNode> {
self.expect_tok(&TokenType::LParen);
let mut params = Vec::new();
while !matches!(self.current().typ, TokenType::RParen | TokenType::Eof) {
if matches!(self.current().typ, TokenType::Comma) {
self.advance();
continue;
}
let name_raw = if let TokenType::Ident(n) = self.current().typ.clone() {
self.advance();
n
} else {
break;
};
let param_type = self.parse_type();
let param_name = go_to_camel(&name_raw);
params.push(ParamNode { name: param_name, param_type });
self.match_tok(&TokenType::Comma);
}
self.expect_tok(&TokenType::RParen);
params
}
fn skip_return_type(&mut self) {
if matches!(self.current().typ, TokenType::LBrace | TokenType::Eof) {
return;
}
let mut depth = 0usize;
while !matches!(self.current().typ, TokenType::Eof) {
match self.current().typ {
TokenType::LParen => { depth += 1; self.advance(); }
TokenType::RParen => {
if depth > 0 { depth -= 1; }
self.advance();
}
TokenType::LBrace => break,
_ => { self.advance(); }
}
}
}
fn parse_block(&mut self) -> Vec<Statement> {
self.expect_tok(&TokenType::LBrace);
let mut stmts = Vec::new();
while !matches!(self.current().typ, TokenType::RBrace | TokenType::Eof) {
self.skip_semis();
if matches!(self.current().typ, TokenType::RBrace | TokenType::Eof) {
break;
}
if let Some(s) = self.parse_statement() {
stmts.push(s);
} else {
self.advance();
}
self.skip_semis();
}
self.match_tok(&TokenType::RBrace);
stmts
}
fn parse_statement(&mut self) -> Option<Statement> {
let loc = self.loc();
match self.current().typ.clone() {
TokenType::If => {
self.advance();
let cond = self.parse_expr()?;
let then_branch = self.parse_block();
let else_branch = if matches!(self.current().typ, TokenType::Else) {
self.advance();
if matches!(self.current().typ, TokenType::If) {
if let Some(s) = self.parse_statement() {
Some(vec![s])
} else {
None
}
} else {
Some(self.parse_block())
}
} else {
None
};
Some(Statement::IfStatement { condition: cond, then_branch, else_branch, source_location: loc })
}
TokenType::For => {
self.advance();
self.parse_for_statement(loc)
}
TokenType::Return => {
self.advance();
let value = if matches!(self.current().typ, TokenType::Semi | TokenType::RBrace | TokenType::Eof) {
None
} else {
self.parse_expr()
};
Some(Statement::ReturnStatement { value, source_location: loc })
}
TokenType::Var | TokenType::Const => {
let is_var = matches!(self.current().typ, TokenType::Var);
self.advance();
let name_raw = self.expect_ident();
let name = go_to_camel(&name_raw);
let var_type = if !matches!(self.current().typ, TokenType::Eq | TokenType::Semi | TokenType::Eof) {
Some(self.parse_type())
} else {
None
};
let init = if matches!(self.current().typ, TokenType::Eq) {
self.advance();
self.parse_expr()?
} else {
return None;
};
Some(Statement::VariableDecl {
name,
var_type,
mutable: is_var,
init,
source_location: loc,
})
}
TokenType::Ident(_) => {
self.parse_ident_statement(loc)
}
TokenType::PlusPlus | TokenType::MinusMinus => {
let is_inc = matches!(self.current().typ, TokenType::PlusPlus);
self.advance();
let operand = self.parse_primary()?;
if is_inc {
Some(Statement::ExpressionStatement {
expression: Expression::IncrementExpr { operand: Box::new(operand), prefix: true },
source_location: loc,
})
} else {
Some(Statement::ExpressionStatement {
expression: Expression::DecrementExpr { operand: Box::new(operand), prefix: true },
source_location: loc,
})
}
}
_ => {
if let Some(expr) = self.parse_expr() {
Some(Statement::ExpressionStatement { expression: expr, source_location: loc })
} else {
None
}
}
}
}
fn parse_ident_statement(&mut self, loc: SourceLocation) -> Option<Statement> {
let lhs = self.parse_expr()?;
match self.current().typ.clone() {
TokenType::ColonEq => {
self.advance();
let rhs = self.parse_expr()?;
let name = if let Expression::Identifier { name } = &lhs {
name.clone()
} else {
String::new()
};
Some(Statement::VariableDecl {
name,
var_type: None,
mutable: true,
init: rhs,
source_location: loc,
})
}
TokenType::Eq => {
self.advance();
let rhs = self.parse_expr()?;
Some(Statement::Assignment { target: lhs, value: rhs, source_location: loc })
}
TokenType::PlusEq => {
self.advance();
let rhs = self.parse_expr()?;
let new_val = Expression::BinaryExpr {
op: BinaryOp::Add,
left: Box::new(lhs.clone()),
right: Box::new(rhs),
};
Some(Statement::Assignment { target: lhs, value: new_val, source_location: loc })
}
TokenType::MinusEq => {
self.advance();
let rhs = self.parse_expr()?;
let new_val = Expression::BinaryExpr {
op: BinaryOp::Sub,
left: Box::new(lhs.clone()),
right: Box::new(rhs),
};
Some(Statement::Assignment { target: lhs, value: new_val, source_location: loc })
}
TokenType::PlusPlus => {
self.advance();
Some(Statement::ExpressionStatement {
expression: Expression::IncrementExpr { operand: Box::new(lhs), prefix: false },
source_location: loc,
})
}
TokenType::MinusMinus => {
self.advance();
Some(Statement::ExpressionStatement {
expression: Expression::DecrementExpr { operand: Box::new(lhs), prefix: false },
source_location: loc,
})
}
_ => {
Some(Statement::ExpressionStatement { expression: lhs, source_location: loc })
}
}
}
fn parse_for_statement(&mut self, loc: SourceLocation) -> Option<Statement> {
let is_three_part = self.has_semi_before_brace();
if is_three_part {
let init = self.parse_for_init_statement()?;
self.skip_semis();
let condition = if matches!(self.current().typ, TokenType::Semi) {
Expression::BoolLiteral { value: true }
} else {
self.parse_expr()?
};
self.skip_semis();
let update = self.parse_for_post_statement()?;
let body = self.parse_block();
Some(Statement::ForStatement {
init: Box::new(init),
condition,
update: Box::new(update),
body,
source_location: loc,
})
} else {
let condition = self.parse_expr()?;
let body = self.parse_block();
let dummy_init = Statement::VariableDecl {
name: "_i".to_string(),
var_type: None,
mutable: true,
init: Expression::BigIntLiteral { value: 0 },
source_location: loc.clone(),
};
let dummy_update = Statement::ExpressionStatement {
expression: Expression::Identifier { name: "_i".to_string() },
source_location: loc.clone(),
};
Some(Statement::ForStatement {
init: Box::new(dummy_init),
condition,
update: Box::new(dummy_update),
body,
source_location: loc,
})
}
}
fn has_semi_before_brace(&self) -> bool {
let mut i = self.pos;
while i < self.tokens.len() {
match self.tokens[i].typ {
TokenType::Semi => return true,
TokenType::LBrace => return false,
TokenType::Eof => return false,
_ => {}
}
i += 1;
}
false
}
fn parse_for_init_statement(&mut self) -> Option<Statement> {
let loc = self.loc();
if let TokenType::Ident(name_raw) = self.current().typ.clone() {
let name = go_to_camel(&name_raw);
self.advance();
if matches!(self.current().typ, TokenType::ColonEq) {
self.advance();
let init = self.parse_expr()?;
return Some(Statement::VariableDecl {
name,
var_type: None,
mutable: true,
init,
source_location: loc,
});
}
}
let expr = self.parse_expr()?;
Some(Statement::ExpressionStatement { expression: expr, source_location: loc })
}
fn parse_for_post_statement(&mut self) -> Option<Statement> {
let loc = self.loc();
let lhs = self.parse_expr()?;
match self.current().typ.clone() {
TokenType::PlusPlus => {
self.advance();
Some(Statement::ExpressionStatement {
expression: Expression::IncrementExpr { operand: Box::new(lhs), prefix: false },
source_location: loc,
})
}
TokenType::MinusMinus => {
self.advance();
Some(Statement::ExpressionStatement {
expression: Expression::DecrementExpr { operand: Box::new(lhs), prefix: false },
source_location: loc,
})
}
TokenType::PlusEq => {
self.advance();
let rhs = self.parse_expr()?;
let new_val = Expression::BinaryExpr {
op: BinaryOp::Add,
left: Box::new(lhs.clone()),
right: Box::new(rhs),
};
Some(Statement::Assignment { target: lhs, value: new_val, source_location: loc })
}
TokenType::MinusEq => {
self.advance();
let rhs = self.parse_expr()?;
let new_val = Expression::BinaryExpr {
op: BinaryOp::Sub,
left: Box::new(lhs.clone()),
right: Box::new(rhs),
};
Some(Statement::Assignment { target: lhs, value: new_val, source_location: loc })
}
_ => Some(Statement::ExpressionStatement { expression: lhs, source_location: loc }),
}
}
fn parse_expr(&mut self) -> Option<Expression> {
self.parse_or()
}
fn parse_or(&mut self) -> Option<Expression> {
let mut left = self.parse_and()?;
while matches!(self.current().typ, TokenType::PipePipe) {
self.advance();
let right = self.parse_and()?;
left = Expression::BinaryExpr { op: BinaryOp::Or, left: Box::new(left), right: Box::new(right) };
}
Some(left)
}
fn parse_and(&mut self) -> Option<Expression> {
let mut left = self.parse_bitor()?;
while matches!(self.current().typ, TokenType::AmpAmp) {
self.advance();
let right = self.parse_bitor()?;
left = Expression::BinaryExpr { op: BinaryOp::And, left: Box::new(left), right: Box::new(right) };
}
Some(left)
}
fn parse_bitor(&mut self) -> Option<Expression> {
let mut left = self.parse_bitxor()?;
while matches!(self.current().typ, TokenType::Pipe) {
self.advance();
let right = self.parse_bitxor()?;
left = Expression::BinaryExpr { op: BinaryOp::BitOr, left: Box::new(left), right: Box::new(right) };
}
Some(left)
}
fn parse_bitxor(&mut self) -> Option<Expression> {
let mut left = self.parse_bitand()?;
while matches!(self.current().typ, TokenType::Caret) {
self.advance();
let right = self.parse_bitand()?;
left = Expression::BinaryExpr { op: BinaryOp::BitXor, left: Box::new(left), right: Box::new(right) };
}
Some(left)
}
fn parse_bitand(&mut self) -> Option<Expression> {
let mut left = self.parse_equality()?;
while matches!(self.current().typ, TokenType::Amp) {
self.advance();
let right = self.parse_equality()?;
left = Expression::BinaryExpr { op: BinaryOp::BitAnd, left: Box::new(left), right: Box::new(right) };
}
Some(left)
}
fn parse_equality(&mut self) -> Option<Expression> {
let mut left = self.parse_relational()?;
loop {
let op = match self.current().typ {
TokenType::EqEq => BinaryOp::StrictEq,
TokenType::BangEq => BinaryOp::StrictNe,
_ => break,
};
self.advance();
let right = self.parse_relational()?;
left = Expression::BinaryExpr { op, left: Box::new(left), right: Box::new(right) };
}
Some(left)
}
fn parse_relational(&mut self) -> Option<Expression> {
let mut left = self.parse_additive()?;
loop {
let op = match self.current().typ {
TokenType::Lt => BinaryOp::Lt,
TokenType::LtEq => BinaryOp::Le,
TokenType::Gt => BinaryOp::Gt,
TokenType::GtEq => BinaryOp::Ge,
_ => break,
};
self.advance();
let right = self.parse_additive()?;
left = Expression::BinaryExpr { op, left: Box::new(left), right: Box::new(right) };
}
Some(left)
}
fn parse_additive(&mut self) -> Option<Expression> {
let mut left = self.parse_multiplicative()?;
loop {
let op = match self.current().typ {
TokenType::Plus => BinaryOp::Add,
TokenType::Minus => BinaryOp::Sub,
_ => break,
};
self.advance();
let right = self.parse_multiplicative()?;
left = Expression::BinaryExpr { op, left: Box::new(left), right: Box::new(right) };
}
Some(left)
}
fn parse_multiplicative(&mut self) -> Option<Expression> {
let mut left = self.parse_unary()?;
loop {
let op = match self.current().typ {
TokenType::Star => BinaryOp::Mul,
TokenType::Slash => BinaryOp::Div,
TokenType::Percent => BinaryOp::Mod,
_ => break,
};
self.advance();
let right = self.parse_unary()?;
left = Expression::BinaryExpr { op, left: Box::new(left), right: Box::new(right) };
}
Some(left)
}
fn parse_unary(&mut self) -> Option<Expression> {
match self.current().typ.clone() {
TokenType::Bang => {
self.advance();
let operand = self.parse_unary()?;
Some(Expression::UnaryExpr { op: UnaryOp::Not, operand: Box::new(operand) })
}
TokenType::Minus => {
self.advance();
let operand = self.parse_unary()?;
Some(Expression::UnaryExpr { op: UnaryOp::Neg, operand: Box::new(operand) })
}
TokenType::Tilde | TokenType::Caret => {
self.advance();
let operand = self.parse_unary()?;
Some(Expression::UnaryExpr { op: UnaryOp::BitNot, operand: Box::new(operand) })
}
_ => self.parse_postfix(),
}
}
fn parse_postfix(&mut self) -> Option<Expression> {
let mut expr = self.parse_primary()?;
loop {
match self.current().typ.clone() {
TokenType::Dot => {
self.advance();
let prop_raw = self.expect_ident();
let prop = go_to_camel(&prop_raw);
if matches!(self.current().typ, TokenType::LParen) {
let args = self.parse_call_args();
expr = Expression::CallExpr {
callee: Box::new(Expression::MemberExpr {
object: Box::new(expr),
property: prop,
}),
args,
};
} else {
expr = Expression::MemberExpr { object: Box::new(expr), property: prop };
}
}
TokenType::LBracket => {
self.advance();
let index = self.parse_expr()?;
self.expect_tok(&TokenType::RBracket);
expr = Expression::IndexAccess { object: Box::new(expr), index: Box::new(index) };
}
TokenType::LParen => {
let args = self.parse_call_args();
expr = Expression::CallExpr { callee: Box::new(expr), args };
}
_ => break,
}
}
Some(expr)
}
fn parse_primary(&mut self) -> Option<Expression> {
match self.current().typ.clone() {
TokenType::Number(n) => {
self.advance();
Some(Expression::BigIntLiteral { value: n })
}
TokenType::True => {
self.advance();
Some(Expression::BoolLiteral { value: true })
}
TokenType::False => {
self.advance();
Some(Expression::BoolLiteral { value: false })
}
TokenType::StringLit(s) => {
self.advance();
Some(Expression::ByteStringLiteral { value: s })
}
TokenType::LParen => {
self.advance();
let expr = self.parse_expr()?;
self.expect_tok(&TokenType::RParen);
Some(expr)
}
TokenType::Ident(name) => {
self.advance();
if name == "runar" && matches!(self.current().typ, TokenType::Dot) {
self.advance(); let member_raw = self.expect_ident();
if matches!(member_raw.as_str(), "Int" | "Bigint" | "Bool") {
if matches!(self.current().typ, TokenType::LParen) {
let args = self.parse_call_args();
if args.len() == 1 {
return Some(args.into_iter().next().unwrap());
}
}
}
let callee_name = map_go_builtin(&member_raw);
if matches!(self.current().typ, TokenType::LParen) {
let args = self.parse_call_args();
return Some(Expression::CallExpr {
callee: Box::new(Expression::Identifier { name: callee_name }),
args,
});
}
return Some(Expression::Identifier { name: callee_name });
}
if (name == self.receiver_name && !self.receiver_name.is_empty())
|| name == "c"
|| name == "self"
{
if matches!(self.current().typ, TokenType::Dot) {
self.advance(); let prop_raw = self.expect_ident();
let prop = go_to_camel(&prop_raw);
if matches!(self.current().typ, TokenType::LParen) {
let args = self.parse_call_args();
return Some(Expression::CallExpr {
callee: Box::new(Expression::MemberExpr {
object: Box::new(Expression::Identifier { name: "this".to_string() }),
property: prop,
}),
args,
});
}
return Some(Expression::PropertyAccess { property: prop });
}
}
let camel = go_to_camel(&name);
Some(Expression::Identifier { name: camel })
}
_ => None,
}
}
fn parse_call_args(&mut self) -> Vec<Expression> {
self.expect_tok(&TokenType::LParen);
let mut args = Vec::new();
while !matches!(self.current().typ, TokenType::RParen | TokenType::Eof) {
if matches!(self.current().typ, TokenType::Comma) {
self.advance();
continue;
}
if let Some(arg) = self.parse_expr() {
args.push(arg);
} else {
break;
}
self.match_tok(&TokenType::Comma);
}
self.expect_tok(&TokenType::RParen);
args
}
fn skip_to_matching_brace(&mut self) {
while !matches!(self.current().typ, TokenType::LBrace | TokenType::Eof) {
self.advance();
}
if matches!(self.current().typ, TokenType::Eof) {
return;
}
let mut depth = 0usize;
while !matches!(self.current().typ, TokenType::Eof) {
match self.current().typ {
TokenType::LBrace => { depth += 1; self.advance(); }
TokenType::RBrace => {
if depth > 0 { depth -= 1; }
self.advance();
if depth == 0 { break; }
}
_ => { self.advance(); }
}
}
}
}