Data update
This commit is contained in:
parent
72eb4943cb
commit
4d5544505c
2347 changed files with 62432 additions and 16731 deletions
|
|
@ -0,0 +1,370 @@
|
|||
#define BUFFER_SIZE 4096
|
||||
|
||||
' Token constants
|
||||
Enum TokensCtes
|
||||
tk_EOI, tk_Mul, tk_Div, tk_Mod, tk_Add, tk_Sub, tk_Negate, tk_Not, tk_Lss, tk_Leq, tk_Gtr
|
||||
tk_Geq, tk_Eql, tk_Neq, tk_Assign, tk_And, tk_Or, tk_If, tk_Else, tk_While, tk_Print
|
||||
tk_Putc, tk_Lparen, tk_Rparen, tk_Lbrace, tk_Rbrace, tk_Semi, tk_Comma, tk_Ident
|
||||
tk_Integer, tk_String
|
||||
End Enum
|
||||
|
||||
' Node types
|
||||
Enum NodeTypes
|
||||
nd_Ident, nd_String, nd_Integer, nd_Sequence, nd_If, nd_Prtc, nd_Prts, nd_Prti, nd_While
|
||||
nd_Assign, nd_Negate, nd_Not, nd_Mul, nd_Div, nd_Mod, nd_Add, nd_Sub, nd_Lss, nd_Leq
|
||||
nd_Gtr, nd_Geq, nd_Eql, nd_Neq, nd_And, nd_Or
|
||||
End Enum
|
||||
|
||||
' Token info structure
|
||||
Type TokenInfo
|
||||
nombre As String
|
||||
rightAssoc As Boolean
|
||||
isBinary As Boolean
|
||||
isUnary As Boolean
|
||||
precedence As Integer
|
||||
nodeType As Integer
|
||||
Declare Constructor()
|
||||
Declare Constructor(n As String, ra As Boolean, ib As Boolean, iu As Boolean, p As Integer, nt As Integer)
|
||||
End Type
|
||||
|
||||
Constructor TokenInfo()
|
||||
nombre = ""
|
||||
rightAssoc = False
|
||||
isBinary = False
|
||||
isUnary = False
|
||||
precedence = 0
|
||||
nodeType = 0
|
||||
End Constructor
|
||||
|
||||
Constructor TokenInfo(n As String, ra As Boolean, ib As Boolean, iu As Boolean, p As Integer, nt As Integer)
|
||||
nombre = n
|
||||
rightAssoc = ra
|
||||
isBinary = ib
|
||||
isUnary = iu
|
||||
precedence = p
|
||||
nodeType = nt
|
||||
End Constructor
|
||||
|
||||
' Node structure
|
||||
Type Node
|
||||
nodeType As Integer
|
||||
izda As Node Ptr
|
||||
dcha As Node Ptr
|
||||
value As String
|
||||
End Type
|
||||
|
||||
' Global variables
|
||||
Dim Shared As Integer ff, errLine, errCol, tok
|
||||
Dim Shared As String tokText, tokOther
|
||||
Dim Shared As TokenInfo tokens(30)
|
||||
|
||||
Declare Function ParseExpr(p As Integer) As Node Ptr
|
||||
|
||||
' Initialize token info
|
||||
Sub InitTokens()
|
||||
tokens(tk_EOI) = TokenInfo("EOI", False, False, False, -1, -1)
|
||||
tokens(tk_Mul) = TokenInfo("*", False, True, False, 13, nd_Mul)
|
||||
tokens(tk_Div) = TokenInfo("/", False, True, False, 13, nd_Div)
|
||||
tokens(tk_Mod) = TokenInfo("%", False, True, False, 13, nd_Mod)
|
||||
tokens(tk_Add) = TokenInfo("+", False, True, False, 12, nd_Add)
|
||||
tokens(tk_Sub) = TokenInfo("-", False, True, False, 12, nd_Sub)
|
||||
tokens(tk_Negate) = TokenInfo("-", False, False, True, 14, nd_Negate)
|
||||
tokens(tk_Not) = TokenInfo("!", False, False, True, 14, nd_Not)
|
||||
tokens(tk_Lss) = TokenInfo("<", False, True, False, 10, nd_Lss)
|
||||
tokens(tk_Leq) = TokenInfo("<=", False, True, False, 10, nd_Leq)
|
||||
tokens(tk_Gtr) = TokenInfo(">", False, True, False, 10, nd_Gtr)
|
||||
tokens(tk_Geq) = TokenInfo(">=", False, True, False, 10, nd_Geq)
|
||||
tokens(tk_Eql) = TokenInfo("==", False, True, False, 9, nd_Eql)
|
||||
tokens(tk_Neq) = TokenInfo("!=", False, True, False, 9, nd_Neq)
|
||||
tokens(tk_Assign) = TokenInfo("=", False, False, False, -1, nd_Assign)
|
||||
tokens(tk_And) = TokenInfo("&&", False, True, False, 5, nd_And)
|
||||
tokens(tk_Or) = TokenInfo("||", False, True, False, 4, nd_Or)
|
||||
tokens(tk_If) = TokenInfo("if", False, False, False, -1, nd_If)
|
||||
tokens(tk_Else) = TokenInfo("else", False, False, False, -1, -1)
|
||||
tokens(tk_While) = TokenInfo("while", False, False, False, -1, nd_While)
|
||||
tokens(tk_Print) = TokenInfo("print", False, False, False, -1, -1)
|
||||
tokens(tk_Putc) = TokenInfo("putc", False, False, False, -1, -1)
|
||||
tokens(tk_Lparen) = TokenInfo("(", False, False, False, -1, -1)
|
||||
tokens(tk_Rparen) = TokenInfo(")", False, False, False, -1, -1)
|
||||
tokens(tk_Lbrace) = TokenInfo("{", False, False, False, -1, -1)
|
||||
tokens(tk_Rbrace) = TokenInfo("}", False, False, False, -1, -1)
|
||||
tokens(tk_Semi) = TokenInfo(";", False, False, False, -1, -1)
|
||||
tokens(tk_Comma) = TokenInfo(",", False, False, False, -1, -1)
|
||||
tokens(tk_Ident) = TokenInfo("Ident", False, False, False, -1, nd_Ident)
|
||||
tokens(tk_Integer) = TokenInfo("Integer", False, False, False, -1, nd_Integer)
|
||||
tokens(tk_String) = TokenInfo("String", False, False, False, -1, nd_String)
|
||||
End Sub
|
||||
|
||||
' show error and exit
|
||||
Sub ShowError(msg As String)
|
||||
Print "(" & errLine & ", " & errCol & ") " & msg
|
||||
End 1
|
||||
End Sub
|
||||
|
||||
' Get next token
|
||||
Sub GetToken()
|
||||
Static As String linea
|
||||
If Eof(ff) Then
|
||||
tok = tk_EOI
|
||||
Exit Sub
|
||||
End If
|
||||
Line Input #ff, linea
|
||||
If Len(linea) = 0 Then ShowError("empty line")
|
||||
|
||||
Dim As Integer numParts = 0
|
||||
Dim As String parts(0 To 3)
|
||||
Dim As String currentPart = ""
|
||||
Dim As Boolean inQuotes = False
|
||||
|
||||
For i As Integer = 1 To Len(linea)
|
||||
Dim As String c = Mid(linea, i, 1)
|
||||
If c = """" Then
|
||||
inQuotes = Not inQuotes
|
||||
currentPart &= c
|
||||
Elseif (Cbool((c = " ") Or (c = Chr(9))) Andalso (inQuotes = False)) Then
|
||||
If Len(currentPart) > 0 Then
|
||||
parts(numParts) = currentPart
|
||||
numParts += 1
|
||||
currentPart = ""
|
||||
End If
|
||||
Else
|
||||
currentPart &= c
|
||||
End If
|
||||
Next
|
||||
If Len(currentPart) > 0 Then
|
||||
parts(numParts) = currentPart
|
||||
numParts += 1
|
||||
End If
|
||||
|
||||
errLine = Valint(parts(0))
|
||||
errCol = Valint(parts(1))
|
||||
tokText = parts(2)
|
||||
tok = -1
|
||||
|
||||
Select Case tokText
|
||||
Case "End_of_input": tok = tk_EOI
|
||||
Case "Op_multiply": tok = tk_Mul
|
||||
Case "Op_divide": tok = tk_Div
|
||||
Case "Op_mod": tok = tk_Mod
|
||||
Case "Op_add": tok = tk_Add
|
||||
Case "Op_subtract": tok = tk_Sub
|
||||
Case "Op_negate": tok = tk_Negate
|
||||
Case "Op_not": tok = tk_Not
|
||||
Case "Op_less": tok = tk_Lss
|
||||
Case "Op_lessequal": tok = tk_Leq
|
||||
Case "Op_greater": tok = tk_Gtr
|
||||
Case "Op_greaterequal":tok = tk_Geq
|
||||
Case "Op_equal": tok = tk_Eql
|
||||
Case "Op_notequal": tok = tk_Neq
|
||||
Case "Op_assign": tok = tk_Assign
|
||||
Case "Op_and": tok = tk_And
|
||||
Case "Op_or": tok = tk_Or
|
||||
Case "Keyword_if": tok = tk_If
|
||||
Case "Keyword_else": tok = tk_Else
|
||||
Case "Keyword_while": tok = tk_While
|
||||
Case "Keyword_print": tok = tk_Print
|
||||
Case "Keyword_putc": tok = tk_Putc
|
||||
Case "LeftParen": tok = tk_Lparen
|
||||
Case "RightParen": tok = tk_Rparen
|
||||
Case "LeftBrace": tok = tk_Lbrace
|
||||
Case "RightBrace": tok = tk_Rbrace
|
||||
Case "Semicolon": tok = tk_Semi
|
||||
Case "Comma": tok = tk_Comma
|
||||
Case "Identifier": tok = tk_Ident
|
||||
Case "Integer": tok = tk_Integer
|
||||
Case "String": tok = tk_String
|
||||
End Select
|
||||
|
||||
If tok = -1 Then ShowError("Unknown token " & tokText)
|
||||
If tok = tk_Integer Or tok = tk_Ident Or tok = tk_String Then
|
||||
tokOther = Iif(numParts >= 4, parts(3), "")
|
||||
End If
|
||||
End Sub
|
||||
|
||||
' Create nodes
|
||||
Function MakeNode(nodeType As Integer, izda As Node Ptr = 0, dcha As Node Ptr = 0) As Node Ptr
|
||||
Dim As Node Ptr n = New Node
|
||||
With *n
|
||||
.nodeType = nodeType
|
||||
.izda = izda
|
||||
.dcha = dcha
|
||||
.value = ""
|
||||
End With
|
||||
|
||||
Return n
|
||||
End Function
|
||||
|
||||
Function MakeLeaf(nodeType As Integer, value As String) As Node Ptr
|
||||
Dim As Node Ptr n = New Node
|
||||
n->nodeType = nodeType
|
||||
n->izda = 0
|
||||
n->dcha = 0
|
||||
n->value = value
|
||||
|
||||
Return n
|
||||
End Function
|
||||
|
||||
Sub Expect(msg As String, s As Integer)
|
||||
If tok = s Then
|
||||
GetToken()
|
||||
Else
|
||||
ShowError(msg & ": Expecting '" & tokens(s).nombre & "', found '" & tokens(tok).nombre & "'")
|
||||
End If
|
||||
End Sub
|
||||
|
||||
' Main parsing functions
|
||||
Function ParenExpr() As Node Ptr
|
||||
Expect("paren_expr", tk_Lparen)
|
||||
Dim As Node Ptr node = ParseExpr(0)
|
||||
Expect("paren_expr", tk_Rparen)
|
||||
|
||||
Return node
|
||||
End Function
|
||||
|
||||
Function ParseExpr(p As Integer) As Node Ptr
|
||||
Dim As Node Ptr x = 0, node
|
||||
Dim As Integer op, q
|
||||
|
||||
Select Case tok
|
||||
Case tk_Lparen
|
||||
x = ParenExpr()
|
||||
Case tk_Sub, tk_Add
|
||||
op = Iif(tok = tk_Sub, tk_Negate, tk_Add)
|
||||
GetToken()
|
||||
node = ParseExpr(tokens(tk_Negate).precedence)
|
||||
x = Iif(op = tk_Negate, MakeNode(nd_Negate, node), node)
|
||||
Case tk_Not
|
||||
GetToken()
|
||||
x = MakeNode(nd_Not, ParseExpr(tokens(tk_Not).precedence))
|
||||
Case tk_Ident
|
||||
x = MakeLeaf(nd_Ident, tokOther)
|
||||
GetToken()
|
||||
Case tk_Integer
|
||||
x = MakeLeaf(nd_Integer, tokOther)
|
||||
GetToken()
|
||||
Case Else
|
||||
ShowError("Expecting a primary, found: " & tokens(tok).nombre)
|
||||
End Select
|
||||
|
||||
While tokens(tok).isBinary Andalso tokens(tok).precedence >= p
|
||||
op = tok
|
||||
GetToken()
|
||||
q = tokens(op).precedence
|
||||
If Not tokens(op).rightAssoc Then q += 1
|
||||
node = ParseExpr(q)
|
||||
x = MakeNode(tokens(op).nodeType, x, node)
|
||||
Wend
|
||||
|
||||
Return x
|
||||
End Function
|
||||
|
||||
Function ParseStmt() As Node Ptr
|
||||
Dim As Node Ptr t = 0, e
|
||||
|
||||
Select Case tok
|
||||
Case tk_If
|
||||
GetToken()
|
||||
e = ParenExpr()
|
||||
Dim As Node Ptr s = ParseStmt()
|
||||
Dim As Node Ptr s2 = 0
|
||||
If tok = tk_Else Then
|
||||
GetToken()
|
||||
s2 = ParseStmt()
|
||||
End If
|
||||
t = MakeNode(nd_If, e, MakeNode(nd_If, s, s2))
|
||||
Case tk_Putc
|
||||
GetToken()
|
||||
e = ParenExpr()
|
||||
t = MakeNode(nd_Prtc, e)
|
||||
Expect("Putc", tk_Semi)
|
||||
Case tk_Print
|
||||
GetToken()
|
||||
Expect("Print", tk_Lparen)
|
||||
Do
|
||||
If tok = tk_String Then
|
||||
e = MakeNode(nd_Prts, MakeLeaf(nd_String, tokOther))
|
||||
GetToken()
|
||||
Else
|
||||
e = MakeNode(nd_Prti, ParseExpr(0))
|
||||
End If
|
||||
t = MakeNode(nd_Sequence, t, e)
|
||||
If tok <> tk_Comma Then Exit Do
|
||||
GetToken()
|
||||
Loop
|
||||
Expect("Print", tk_Rparen)
|
||||
Expect("Print", tk_Semi)
|
||||
Case tk_Semi
|
||||
GetToken()
|
||||
Case tk_Ident
|
||||
Dim As Node Ptr v = MakeLeaf(nd_Ident, tokOther)
|
||||
GetToken()
|
||||
Expect("assign", tk_Assign)
|
||||
e = ParseExpr(0)
|
||||
t = MakeNode(nd_Assign, v, e)
|
||||
Expect("assign", tk_Semi)
|
||||
Case tk_While
|
||||
GetToken()
|
||||
e = ParenExpr()
|
||||
Dim As Node Ptr s = ParseStmt()
|
||||
t = MakeNode(nd_While, e, s)
|
||||
Case tk_Lbrace
|
||||
GetToken()
|
||||
While tok <> tk_Rbrace Andalso tok <> tk_EOI
|
||||
t = MakeNode(nd_Sequence, t, ParseStmt())
|
||||
Wend
|
||||
Expect("Lbrace", tk_Rbrace)
|
||||
Case tk_EOI
|
||||
' Do nothing
|
||||
Case Else
|
||||
ShowError("Expecting start of statement, found: " & tokens(tok).nombre)
|
||||
End Select
|
||||
|
||||
Return t
|
||||
End Function
|
||||
|
||||
Function Parse() As Node Ptr
|
||||
Dim As Node Ptr tree = 0
|
||||
|
||||
GetToken()
|
||||
While tok <> tk_EOI
|
||||
tree = MakeNode(nd_Sequence, tree, ParseStmt())
|
||||
Wend
|
||||
|
||||
Return tree
|
||||
End Function
|
||||
|
||||
Sub PrintAST(t As Node Ptr)
|
||||
Dim As String displayNodes(24) = { _
|
||||
"Identifier","String","Integer","Sequence","If","Prtc", _
|
||||
"Prts","Prti","While","Assign","Negate","Not","Multiply","Divide","Mod", _
|
||||
"Add","Subtract","Less","LessEqual","Greater","GreaterEqual","Equal", _
|
||||
"NotEqual","And","Or" }
|
||||
|
||||
If t = 0 Then
|
||||
Print ";"
|
||||
Else
|
||||
Print Left(displayNodes(t->nodeType) & Space(14), 14);
|
||||
Select Case t->nodeType
|
||||
Case nd_Ident, nd_Integer, nd_String
|
||||
Print t->value
|
||||
Case Else
|
||||
Print
|
||||
PrintAST(t->izda)
|
||||
PrintAST(t->dcha)
|
||||
End Select
|
||||
End If
|
||||
End Sub
|
||||
|
||||
' Main program
|
||||
InitTokens()
|
||||
If Command(1) <> "" Then
|
||||
ff = Freefile
|
||||
Open Command(1) For Input Access Read As #ff Len = BUFFER_SIZE
|
||||
Else
|
||||
ff = 1 ' stdin
|
||||
End If
|
||||
|
||||
Dim As Node Ptr ast = Parse()
|
||||
PrintAST(ast)
|
||||
|
||||
Sleep
|
||||
|
|
@ -97,7 +97,7 @@ Module syntax_analyzer(b$){
|
|||
sub stmt(&op)
|
||||
local t=(,)
|
||||
if op=Identifier then
|
||||
t=(line$)
|
||||
t=(line$,)
|
||||
getone(&op)
|
||||
expected(Op_assign)
|
||||
getone(&op)
|
||||
|
|
|
|||
659
Task/Compiler-syntax-analyzer/Rust/compiler-syntax-analyzer.rs
Normal file
659
Task/Compiler-syntax-analyzer/Rust/compiler-syntax-analyzer.rs
Normal file
|
|
@ -0,0 +1,659 @@
|
|||
// src/main.rs
|
||||
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::io::{self, prelude::*, BufReader, BufWriter, Lines};
|
||||
use std::process::exit;
|
||||
use std::collections::HashMap; // For potentially faster token lookup if needed
|
||||
|
||||
// --- Enums ---
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] // Added Hash for HashMap key
|
||||
pub enum TokenType {
|
||||
Eoi, Mul, Div, Mod, Add, Sub, Negate, Not, Lss, Leq, Gtr,
|
||||
Geq, Eql, Neq, Assign, And, Or, If, Else, While, Print,
|
||||
Putc, Lparen, Rparen, Lbrace, Rbrace, Semi, Comma, Ident,
|
||||
Integer, String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum NodeType {
|
||||
Ident, String, Integer, Sequence, If, Prtc, Prts, Prti, While,
|
||||
Assign, Negate, Not, Mul, Div, Mod, Add, Sub, Lss, Leq,
|
||||
Gtr, Geq, Eql, Neq, And, Or,
|
||||
}
|
||||
|
||||
// --- Structs ---
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Token {
|
||||
tok_type: TokenType,
|
||||
err_ln: usize,
|
||||
err_col: usize,
|
||||
text: Option<String>, // Use Option<String> for optional text
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AstNode {
|
||||
node_type: NodeType,
|
||||
// Use Option<Box<T>> for optional, heap-allocated children
|
||||
left: Option<Box<AstNode>>,
|
||||
right: Option<Box<AstNode>>,
|
||||
// Use Option<String> for optional value (ident, literal)
|
||||
value: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TokenAttributes {
|
||||
text: &'static str,
|
||||
enum_text: &'static str,
|
||||
tok_type: TokenType,
|
||||
right_associative: bool,
|
||||
is_binary: bool,
|
||||
is_unary: bool,
|
||||
precedence: Option<u8>, // Use Option for precedence (-1 in C)
|
||||
node_type: Option<NodeType>, // Use Option for node type (-1 in C)
|
||||
}
|
||||
|
||||
// --- Static Data ---
|
||||
|
||||
// Use 'static lifetime for compile-time constants
|
||||
// Order MUST match TokenType enum definition
|
||||
const TOKEN_ATTRIBUTES: &[TokenAttributes] = &[
|
||||
TokenAttributes { text: "EOI", enum_text: "End_of_input", tok_type: TokenType::Eoi, right_associative: false, is_binary: false, is_unary: false, precedence: None, node_type: None },
|
||||
TokenAttributes { text: "*", enum_text: "Op_multiply", tok_type: TokenType::Mul, right_associative: false, is_binary: true, is_unary: false, precedence: Some(13), node_type: Some(NodeType::Mul) },
|
||||
TokenAttributes { text: "/", enum_text: "Op_divide", tok_type: TokenType::Div, right_associative: false, is_binary: true, is_unary: false, precedence: Some(13), node_type: Some(NodeType::Div) },
|
||||
TokenAttributes { text: "%", enum_text: "Op_mod", tok_type: TokenType::Mod, right_associative: false, is_binary: true, is_unary: false, precedence: Some(13), node_type: Some(NodeType::Mod) },
|
||||
TokenAttributes { text: "+", enum_text: "Op_add", tok_type: TokenType::Add, right_associative: false, is_binary: true, is_unary: false, precedence: Some(12), node_type: Some(NodeType::Add) },
|
||||
TokenAttributes { text: "-", enum_text: "Op_subtract", tok_type: TokenType::Sub, right_associative: false, is_binary: true, is_unary: false, precedence: Some(12), node_type: Some(NodeType::Sub) },
|
||||
TokenAttributes { text: "-", enum_text: "Op_negate", tok_type: TokenType::Negate, right_associative: false, is_binary: false, is_unary: true, precedence: Some(14), node_type: Some(NodeType::Negate) }, // NOTE: C code listed Sub/Add precedence here, but Negate makes more sense
|
||||
TokenAttributes { text: "!", enum_text: "Op_not", tok_type: TokenType::Not, right_associative: false, is_binary: false, is_unary: true, precedence: Some(14), node_type: Some(NodeType::Not) },
|
||||
TokenAttributes { text: "<", enum_text: "Op_less", tok_type: TokenType::Lss, right_associative: false, is_binary: true, is_unary: false, precedence: Some(10), node_type: Some(NodeType::Lss) },
|
||||
TokenAttributes { text: "<=", enum_text: "Op_lessequal", tok_type: TokenType::Leq, right_associative: false, is_binary: true, is_unary: false, precedence: Some(10), node_type: Some(NodeType::Leq) },
|
||||
TokenAttributes { text: ">", enum_text: "Op_greater", tok_type: TokenType::Gtr, right_associative: false, is_binary: true, is_unary: false, precedence: Some(10), node_type: Some(NodeType::Gtr) },
|
||||
TokenAttributes { text: ">=", enum_text: "Op_greaterequal", tok_type: TokenType::Geq, right_associative: false, is_binary: true, is_unary: false, precedence: Some(10), node_type: Some(NodeType::Geq) },
|
||||
TokenAttributes { text: "==", enum_text: "Op_equal", tok_type: TokenType::Eql, right_associative: false, is_binary: true, is_unary: false, precedence: Some(9), node_type: Some(NodeType::Eql) },
|
||||
TokenAttributes { text: "!=", enum_text: "Op_notequal", tok_type: TokenType::Neq, right_associative: false, is_binary: true, is_unary: false, precedence: Some(9), node_type: Some(NodeType::Neq) },
|
||||
TokenAttributes { text: "=", enum_text: "Op_assign", tok_type: TokenType::Assign, right_associative: false, is_binary: false, is_unary: false, precedence: None, node_type: Some(NodeType::Assign) }, // Assignment is special, not a standard binary op in expr parsing
|
||||
TokenAttributes { text: "&&", enum_text: "Op_and", tok_type: TokenType::And, right_associative: false, is_binary: true, is_unary: false, precedence: Some(5), node_type: Some(NodeType::And) },
|
||||
TokenAttributes { text: "||", enum_text: "Op_or", tok_type: TokenType::Or, right_associative: false, is_binary: true, is_unary: false, precedence: Some(4), node_type: Some(NodeType::Or) },
|
||||
TokenAttributes { text: "if", enum_text: "Keyword_if", tok_type: TokenType::If, right_associative: false, is_binary: false, is_unary: false, precedence: None, node_type: Some(NodeType::If) },
|
||||
TokenAttributes { text: "else", enum_text: "Keyword_else", tok_type: TokenType::Else, right_associative: false, is_binary: false, is_unary: false, precedence: None, node_type: None },
|
||||
TokenAttributes { text: "while", enum_text: "Keyword_while", tok_type: TokenType::While, right_associative: false, is_binary: false, is_unary: false, precedence: None, node_type: Some(NodeType::While) },
|
||||
TokenAttributes { text: "print", enum_text: "Keyword_print", tok_type: TokenType::Print, right_associative: false, is_binary: false, is_unary: false, precedence: None, node_type: None },
|
||||
TokenAttributes { text: "putc", enum_text: "Keyword_putc", tok_type: TokenType::Putc, right_associative: false, is_binary: false, is_unary: false, precedence: None, node_type: None },
|
||||
TokenAttributes { text: "(", enum_text: "LeftParen", tok_type: TokenType::Lparen, right_associative: false, is_binary: false, is_unary: false, precedence: None, node_type: None },
|
||||
TokenAttributes { text: ")", enum_text: "RightParen", tok_type: TokenType::Rparen, right_associative: false, is_binary: false, is_unary: false, precedence: None, node_type: None },
|
||||
TokenAttributes { text: "{", enum_text: "LeftBrace", tok_type: TokenType::Lbrace, right_associative: false, is_binary: false, is_unary: false, precedence: None, node_type: None },
|
||||
TokenAttributes { text: "}", enum_text: "RightBrace", tok_type: TokenType::Rbrace, right_associative: false, is_binary: false, is_unary: false, precedence: None, node_type: None },
|
||||
TokenAttributes { text: ";", enum_text: "Semicolon", tok_type: TokenType::Semi, right_associative: false, is_binary: false, is_unary: false, precedence: None, node_type: None },
|
||||
TokenAttributes { text: ",", enum_text: "Comma", tok_type: TokenType::Comma, right_associative: false, is_binary: false, is_unary: false, precedence: None, node_type: None },
|
||||
TokenAttributes { text: "Ident", enum_text: "Identifier", tok_type: TokenType::Ident, right_associative: false, is_binary: false, is_unary: false, precedence: None, node_type: Some(NodeType::Ident) },
|
||||
TokenAttributes { text: "Integer literal", enum_text: "Integer", tok_type: TokenType::Integer, right_associative: false, is_binary: false, is_unary: false, precedence: None, node_type: Some(NodeType::Integer) },
|
||||
TokenAttributes { text: "String literal", enum_text: "String", tok_type: TokenType::String, right_associative: false, is_binary: false, is_unary: false, precedence: None, node_type: Some(NodeType::String) },
|
||||
];
|
||||
|
||||
// Create a lazy-initialized HashMap for faster lookup by enum_text
|
||||
use lazy_static::lazy_static; // Add `lazy_static = "1.4.0"` to Cargo.toml
|
||||
lazy_static! {
|
||||
static ref TOKEN_MAP_BY_ENUM_TEXT: HashMap<&'static str, &'static TokenAttributes> = {
|
||||
let mut m = HashMap::new();
|
||||
for attr in TOKEN_ATTRIBUTES {
|
||||
m.insert(attr.enum_text, attr);
|
||||
}
|
||||
m
|
||||
};
|
||||
static ref TOKEN_MAP_BY_TYPE: HashMap<TokenType, &'static TokenAttributes> = {
|
||||
let mut m = HashMap::new();
|
||||
for attr in TOKEN_ATTRIBUTES {
|
||||
m.insert(attr.tok_type, attr);
|
||||
}
|
||||
m
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
const DISPLAY_NODES: &[&str] = &[
|
||||
"Identifier", "String", "Integer", "Sequence", "If", "Prtc",
|
||||
"Prts", "Prti", "While", "Assign", "Negate", "Not", "Multiply", "Divide", "Mod",
|
||||
"Add", "Subtract", "Less", "LessEqual", "Greater", "GreaterEqual", "Equal",
|
||||
"NotEqual", "And", "Or",
|
||||
];
|
||||
|
||||
// --- Error Handling ---
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ParseError {
|
||||
Io(io::Error),
|
||||
UnexpectedToken {
|
||||
expected: String, // Can describe expected category or specific token
|
||||
found: Token,
|
||||
context: &'static str,
|
||||
},
|
||||
InvalidTokenFormat(usize, String), // line_num, line_content
|
||||
UnknownTokenName(String),
|
||||
EndOfInput,
|
||||
Custom(String),
|
||||
}
|
||||
|
||||
// Allow converting io::Error into ParseError
|
||||
impl From<io::Error> for ParseError {
|
||||
fn from(err: io::Error) -> Self {
|
||||
ParseError::Io(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ParseError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ParseError::Io(err) => write!(f, "I/O Error: {}", err),
|
||||
ParseError::UnexpectedToken { expected, found, context } => {
|
||||
let found_attr = TOKEN_MAP_BY_TYPE.get(&found.tok_type).map_or("?", |a| a.text);
|
||||
write!(
|
||||
f,
|
||||
"({}, {}) Parse Error in {}: Expected {}, found '{}' ({:?})",
|
||||
found.err_ln, found.err_col, context, expected, found_attr, found.tok_type
|
||||
)
|
||||
}
|
||||
ParseError::InvalidTokenFormat(line_num, line) => {
|
||||
write!(f, "Invalid token format on line {}: '{}'", line_num, line)
|
||||
}
|
||||
ParseError::UnknownTokenName(name) => write!(f, "Unknown token name: {}", name),
|
||||
ParseError::EndOfInput => write!(f, "Unexpected end of input"),
|
||||
ParseError::Custom(msg) => write!(f, "Error: {}", msg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for ParseError {}
|
||||
|
||||
type ParseResult<T> = Result<T, ParseError>;
|
||||
|
||||
// --- Helper Functions ---
|
||||
|
||||
fn get_token_attr(token_type: TokenType) -> &'static TokenAttributes {
|
||||
// Using HashMap lookup now
|
||||
TOKEN_MAP_BY_TYPE.get(&token_type)
|
||||
.unwrap_or_else(|| panic!("Internal error: Missing attributes for token type {:?}", token_type)) // Should not happen if maps are correct
|
||||
}
|
||||
|
||||
fn make_node(node_type: NodeType, left: Option<Box<AstNode>>, right: Option<Box<AstNode>>) -> Box<AstNode> {
|
||||
Box::new(AstNode {
|
||||
node_type,
|
||||
left,
|
||||
right,
|
||||
value: None,
|
||||
})
|
||||
}
|
||||
|
||||
fn make_leaf(node_type: NodeType, value: String) -> Box<AstNode> {
|
||||
Box::new(AstNode {
|
||||
node_type,
|
||||
left: None,
|
||||
right: None,
|
||||
value: Some(value),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// --- Parser Struct ---
|
||||
|
||||
struct Parser<R: BufRead> {
|
||||
// Use Lines iterator for easy line reading
|
||||
lines: Lines<R>,
|
||||
current_line_num: usize,
|
||||
// Store the *next* token to be processed (lookahead)
|
||||
current_token: Token,
|
||||
}
|
||||
|
||||
impl<R: BufRead> Parser<R> {
|
||||
fn new(reader: R) -> ParseResult<Self> {
|
||||
let mut parser = Parser {
|
||||
lines: reader.lines(),
|
||||
current_line_num: 0,
|
||||
// Initialize with a dummy EOI token before the first read
|
||||
current_token: Token {
|
||||
tok_type: TokenType::Eoi,
|
||||
err_ln: 0,
|
||||
err_col: 0,
|
||||
text: None,
|
||||
},
|
||||
};
|
||||
// Read the first actual token
|
||||
parser.next_token()?;
|
||||
Ok(parser)
|
||||
}
|
||||
|
||||
// Reads the next line and parses it into a token, updating self.current_token
|
||||
fn next_token(&mut self) -> ParseResult<()> {
|
||||
match self.lines.next() {
|
||||
Some(Ok(line)) => {
|
||||
self.current_line_num += 1;
|
||||
let trimmed_line = line.trim_end(); // Keep original line for error msg
|
||||
if trimmed_line.is_empty() {
|
||||
// Skip empty lines potentially introduced by trimming or blank lines
|
||||
return self.next_token();
|
||||
}
|
||||
|
||||
let mut parts = trimmed_line.splitn(4, |c: char| c.is_whitespace());
|
||||
|
||||
let err_ln_str = parts.next();
|
||||
let err_col_str = parts.next();
|
||||
let name_str = parts.next();
|
||||
let remaining = parts.next().unwrap_or("").trim_start(); // Rest of the line is optional value
|
||||
|
||||
|
||||
// Use if let for cleaner parsing and error handling
|
||||
if let (Some(ln_s), Some(col_s), Some(name)) = (err_ln_str, err_col_str, name_str) {
|
||||
let err_ln = ln_s.parse::<usize>()
|
||||
.map_err(|_| ParseError::InvalidTokenFormat(self.current_line_num, line.clone()))?;
|
||||
let err_col = col_s.parse::<usize>()
|
||||
.map_err(|_| ParseError::InvalidTokenFormat(self.current_line_num, line.clone()))?;
|
||||
|
||||
// Lookup token type from name
|
||||
let attributes = TOKEN_MAP_BY_ENUM_TEXT.get(name)
|
||||
.ok_or_else(|| ParseError::UnknownTokenName(name.to_string()))?;
|
||||
|
||||
let text = if !remaining.is_empty() {
|
||||
Some(remaining.to_string())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
self.current_token = Token {
|
||||
tok_type: attributes.tok_type,
|
||||
err_ln,
|
||||
err_col,
|
||||
text,
|
||||
};
|
||||
Ok(())
|
||||
|
||||
} else {
|
||||
Err(ParseError::InvalidTokenFormat(self.current_line_num, line))
|
||||
}
|
||||
}
|
||||
Some(Err(e)) => Err(ParseError::Io(e)),
|
||||
None => {
|
||||
// End of input, set token to EOI
|
||||
self.current_token = Token {
|
||||
tok_type: TokenType::Eoi,
|
||||
// Use last known line/col or defaults if file was empty
|
||||
err_ln: self.current_token.err_ln,
|
||||
err_col: self.current_token.err_col,
|
||||
text: None,
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check current token type, consume it if it matches, otherwise return error
|
||||
fn expect(&mut self, expected: TokenType, context: &'static str) -> ParseResult<()> {
|
||||
if self.current_token.tok_type == expected {
|
||||
self.next_token()?;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(ParseError::UnexpectedToken {
|
||||
expected: format!("'{}' ({:?})", get_token_attr(expected).text, expected),
|
||||
found: self.current_token.clone(), // Clone token for error reporting
|
||||
context,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Check current token type *without* consuming it
|
||||
fn check(&self, expected: TokenType) -> bool {
|
||||
self.current_token.tok_type == expected
|
||||
}
|
||||
|
||||
// Get the current token's text value, expecting it to exist
|
||||
fn consume_text(&mut self, context: &'static str) -> ParseResult<String> {
|
||||
let token = self.current_token.clone(); // Clone before consuming
|
||||
let text = token.text.ok_or_else(|| ParseError::Custom(
|
||||
format!("({},{}) Expected text value for token {:?} in {}",
|
||||
token.err_ln, token.err_col, token.tok_type, context)
|
||||
))?;
|
||||
self.next_token()?; // Consume the token
|
||||
Ok(text)
|
||||
}
|
||||
|
||||
|
||||
// --- Parsing Methods (Expression & Statement) ---
|
||||
|
||||
// expr: Parse expressions using precedence climbing
|
||||
fn expr(&mut self, min_precedence: u8) -> ParseResult<Box<AstNode>> {
|
||||
let mut left = self.parse_primary()?;
|
||||
|
||||
// Precedence climbing loop
|
||||
loop {
|
||||
let current_tok_type = self.current_token.tok_type;
|
||||
let current_attr = get_token_attr(current_tok_type);
|
||||
|
||||
// Stop if token is not a binary operator OR its precedence is too low
|
||||
if !current_attr.is_binary || current_attr.precedence.unwrap_or(0) < min_precedence {
|
||||
break;
|
||||
}
|
||||
|
||||
let op_type = current_tok_type;
|
||||
let precedence = current_attr.precedence.unwrap(); // Known because is_binary is true
|
||||
let node_type = current_attr.node_type.expect("Binary operator must have node type");
|
||||
|
||||
// Consume operator token
|
||||
self.next_token()?;
|
||||
|
||||
// Determine precedence for recursive call (handle associativity)
|
||||
let next_min_precedence = if current_attr.right_associative {
|
||||
precedence
|
||||
} else {
|
||||
precedence + 1
|
||||
};
|
||||
|
||||
let right = self.expr(next_min_precedence)?;
|
||||
left = make_node(node_type, Some(left), Some(right));
|
||||
}
|
||||
|
||||
Ok(left)
|
||||
}
|
||||
|
||||
// Parse primary expressions (literals, identifiers, unary ops, parentheses)
|
||||
fn parse_primary(&mut self) -> ParseResult<Box<AstNode>> {
|
||||
let token = self.current_token.clone(); // Clone token info before consuming
|
||||
|
||||
match token.tok_type {
|
||||
TokenType::Lparen => self.paren_expr(),
|
||||
TokenType::Sub | TokenType::Add => { // Unary +/-
|
||||
self.next_token()?; // Consume '-' or '+'
|
||||
let node = self.expr(get_token_attr(TokenType::Negate).precedence.unwrap())?; // Use Negate's precedence
|
||||
if token.tok_type == TokenType::Sub {
|
||||
Ok(make_node(NodeType::Negate, Some(node), None))
|
||||
} else {
|
||||
Ok(node) // Unary plus is identity
|
||||
}
|
||||
}
|
||||
TokenType::Not => { // Unary !
|
||||
self.next_token()?; // Consume '!'
|
||||
let node = self.expr(get_token_attr(TokenType::Not).precedence.unwrap())?;
|
||||
Ok(make_node(NodeType::Not, Some(node), None))
|
||||
}
|
||||
TokenType::Ident => {
|
||||
let ident_name = self.consume_text("identifier")?;
|
||||
Ok(make_leaf(NodeType::Ident, ident_name))
|
||||
}
|
||||
TokenType::Integer => {
|
||||
let int_val = self.consume_text("integer literal")?;
|
||||
Ok(make_leaf(NodeType::Integer, int_val))
|
||||
}
|
||||
// String literals only appear in 'print' in this grammar?
|
||||
// If they could be primary expressions, add:
|
||||
// TokenType::String => {
|
||||
// let str_val = self.consume_text("string literal")?;
|
||||
// Ok(make_leaf(NodeType::String, str_val))
|
||||
// }
|
||||
_ => Err(ParseError::UnexpectedToken{
|
||||
expected: "primary expression (literal, identifier, unary op, or '(')".to_string(),
|
||||
found: token,
|
||||
context: "expr",
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
// paren_expr: Parse parenthesized expressions
|
||||
fn paren_expr(&mut self) -> ParseResult<Box<AstNode>> {
|
||||
self.expect(TokenType::Lparen, "paren_expr start")?;
|
||||
let node = self.expr(0)?; // Start with lowest precedence inside parens
|
||||
self.expect(TokenType::Rparen, "paren_expr end")?;
|
||||
Ok(node)
|
||||
}
|
||||
|
||||
// stmt: Parse a single statement
|
||||
// Returns Option<Box<AstNode>> because ';' might result in no node
|
||||
fn stmt(&mut self) -> ParseResult<Option<Box<AstNode>>> {
|
||||
match self.current_token.tok_type {
|
||||
TokenType::If => {
|
||||
self.next_token()?; // Consume 'if'
|
||||
let condition = self.paren_expr()?;
|
||||
let then_branch = self.stmt()?.ok_or_else(|| ParseError::Custom(
|
||||
"Expected statement after 'if' condition".to_string()
|
||||
))?; // If requires a statement
|
||||
|
||||
let else_branch = if self.check(TokenType::Else) {
|
||||
self.next_token()?; // Consume 'else'
|
||||
Some(self.stmt()?.ok_or_else(|| ParseError::Custom(
|
||||
"Expected statement after 'else'".to_string()
|
||||
))?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Structure: If(condition, If(then, else)) - mimics C structure
|
||||
let if_node = make_node(NodeType::If, Some(then_branch), else_branch);
|
||||
Ok(Some(make_node(NodeType::If, Some(condition), Some(if_node))))
|
||||
}
|
||||
TokenType::Putc => {
|
||||
self.next_token()?; // Consume 'putc'
|
||||
let arg = self.paren_expr()?;
|
||||
let node = make_node(NodeType::Prtc, Some(arg), None);
|
||||
self.expect(TokenType::Semi, "putc statement")?;
|
||||
Ok(Some(node))
|
||||
}
|
||||
TokenType::Print => {
|
||||
self.next_token()?; // Consume 'print'
|
||||
self.expect(TokenType::Lparen, "print statement start")?;
|
||||
|
||||
let mut sequence: Option<Box<AstNode>> = None;
|
||||
|
||||
loop {
|
||||
let expr_node = if self.check(TokenType::String) {
|
||||
let str_val = self.consume_text("print string literal")?;
|
||||
make_node(NodeType::Prts, Some(make_leaf(NodeType::String, str_val)), None)
|
||||
} else {
|
||||
make_node(NodeType::Prti, Some(self.expr(0)?), None)
|
||||
};
|
||||
|
||||
// Build sequence: Sequence(prev_sequence, current_print_expr)
|
||||
sequence = Some(make_node(NodeType::Sequence, sequence, Some(expr_node)));
|
||||
|
||||
if !self.check(TokenType::Comma) {
|
||||
break; // Exit loop if no more commas
|
||||
}
|
||||
self.next_token()?; // Consume ','
|
||||
}
|
||||
|
||||
self.expect(TokenType::Rparen, "print statement end")?;
|
||||
self.expect(TokenType::Semi, "print statement")?;
|
||||
Ok(sequence) // Return the sequence of print nodes
|
||||
}
|
||||
TokenType::Semi => {
|
||||
self.next_token()?; // Consume ';'
|
||||
Ok(None) // Empty statement produces no node
|
||||
}
|
||||
TokenType::Ident => {
|
||||
let ident_token = self.current_token.clone();
|
||||
let ident_name = ident_token.text.ok_or_else(|| ParseError::Custom(
|
||||
"Internal error: Identifier token missing text".to_string()
|
||||
))?;
|
||||
self.next_token()?; // Consume identifier
|
||||
|
||||
self.expect(TokenType::Assign, "assignment")?;
|
||||
let value_expr = self.expr(0)?; // Assignment has low precedence effectively
|
||||
|
||||
let var_node = make_leaf(NodeType::Ident, ident_name);
|
||||
let assign_node = make_node(NodeType::Assign, Some(var_node), Some(value_expr));
|
||||
|
||||
self.expect(TokenType::Semi, "assignment statement")?;
|
||||
Ok(Some(assign_node))
|
||||
}
|
||||
TokenType::While => {
|
||||
self.next_token()?; // Consume 'while'
|
||||
let condition = self.paren_expr()?;
|
||||
let body = self.stmt()?.ok_or_else(|| ParseError::Custom(
|
||||
"Expected statement for 'while' body".to_string()
|
||||
))?;
|
||||
Ok(Some(make_node(NodeType::While, Some(condition), Some(body))))
|
||||
}
|
||||
TokenType::Lbrace => {
|
||||
self.next_token()?; // Consume '{'
|
||||
let mut sequence: Option<Box<AstNode>> = None;
|
||||
while !self.check(TokenType::Rbrace) && !self.check(TokenType::Eoi) {
|
||||
if let Some(stmt_node) = self.stmt()? {
|
||||
// Build sequence: Sequence(prev_sequence, current_statement)
|
||||
sequence = Some(make_node(NodeType::Sequence, sequence, Some(stmt_node)));
|
||||
}
|
||||
}
|
||||
self.expect(TokenType::Rbrace, "block statement end")?;
|
||||
Ok(sequence) // Return the sequence of statements in the block
|
||||
}
|
||||
TokenType::Eoi => Ok(None), // End of input is valid here
|
||||
_ => Err(ParseError::UnexpectedToken {
|
||||
expected: "start of statement (if, while, print, putc, {, ;, identifier)".to_string(),
|
||||
found: self.current_token.clone(),
|
||||
context: "stmt",
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
// parse: Main parsing loop, creates sequence of statements
|
||||
fn parse(&mut self) -> ParseResult<Box<AstNode>> {
|
||||
let mut program_sequence: Option<Box<AstNode>> = None;
|
||||
|
||||
// Loop until End Of Input is the *current* token
|
||||
while !self.check(TokenType::Eoi) {
|
||||
match self.stmt()? {
|
||||
Some(stmt_node) => {
|
||||
// Build sequence: Sequence(prev_sequence, current_statement)
|
||||
program_sequence = Some(make_node(NodeType::Sequence, program_sequence, Some(stmt_node)));
|
||||
}
|
||||
None => {
|
||||
// Handle empty statements (like ';') - they don't add to the sequence
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If program_sequence is still None (empty input or only ';'), create an empty sequence.
|
||||
// Otherwise, return the built sequence.
|
||||
Ok(program_sequence.unwrap_or_else(|| make_node(NodeType::Sequence, None, None)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// --- AST Printing ---
|
||||
|
||||
fn prt_ast(node: Option<&AstNode>, indent: usize) {
|
||||
let prefix = " ".repeat(indent);
|
||||
match node {
|
||||
None => {
|
||||
// Represent null children clearly, maybe matching the C output style
|
||||
println!("{}<null>", prefix);
|
||||
// Or match C output: println!("{};", prefix); but <null> is clearer
|
||||
}
|
||||
Some(t) => {
|
||||
// Use DISPLAY_NODES array, ensure NodeType enum indices match
|
||||
let node_name = DISPLAY_NODES.get(t.node_type as usize).unwrap_or(&"UnknownNode");
|
||||
|
||||
match t.node_type {
|
||||
NodeType::Ident | NodeType::Integer | NodeType::String => {
|
||||
println!("{}{:<14} {}", prefix, node_name, t.value.as_deref().unwrap_or(""));
|
||||
}
|
||||
_ => {
|
||||
println!("{}{:<14}", prefix, node_name);
|
||||
prt_ast(t.left.as_deref(), indent + 1);
|
||||
prt_ast(t.right.as_deref(), indent + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// --- Main Function ---
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
|
||||
// --- I/O Setup ---
|
||||
// Use Box<dyn Read> to handle both stdin and files
|
||||
let reader: Box<dyn Read> = if args.len() > 1 && !args[1].is_empty() {
|
||||
Box::new(File::open(&args[1])
|
||||
.map_err(|e| format!("Error opening input file '{}': {}", args[1], e))?)
|
||||
} else {
|
||||
Box::new(io::stdin())
|
||||
};
|
||||
let buf_reader = BufReader::new(reader);
|
||||
|
||||
// Use Box<dyn Write> for output flexibility (though stdout is typical)
|
||||
let writer: Box<dyn Write> = if args.len() > 2 && !args[2].is_empty() {
|
||||
Box::new(File::create(&args[2])
|
||||
.map_err(|e| format!("Error creating output file '{}': {}", args[2], e))?)
|
||||
} else {
|
||||
Box::new(io::stdout())
|
||||
};
|
||||
let mut buf_writer = BufWriter::new(writer); // Not strictly needed for AST printing, but good practice
|
||||
|
||||
// --- Parsing ---
|
||||
// Create parser and run it
|
||||
let mut parser = Parser::new(buf_reader)
|
||||
.map_err(|e| format!("Parser initialization failed: {}", e))?; // Convert ParseError for main's return
|
||||
|
||||
match parser.parse() {
|
||||
Ok(ast_root) => {
|
||||
// --- AST Printing ---
|
||||
// Use a closure to capture buf_writer for printing
|
||||
let print_to_writer = |node: Option<&AstNode>, indent: usize| {
|
||||
let prefix = " ".repeat(indent);
|
||||
match node {
|
||||
None => writeln!(buf_writer, "{}<null>", prefix).unwrap(), // Handle write errors if needed
|
||||
Some(t) => {
|
||||
let node_name = DISPLAY_NODES.get(t.node_type as usize).unwrap_or(&"UnknownNode");
|
||||
match t.node_type {
|
||||
NodeType::Ident | NodeType::Integer | NodeType::String => {
|
||||
writeln!(buf_writer, "{}{:<14} {}", prefix, node_name, t.value.as_deref().unwrap_or("")).unwrap();
|
||||
}
|
||||
_ => {
|
||||
writeln!(buf_writer, "{}{:<14}", prefix, node_name).unwrap();
|
||||
// Recursive calls need access to buf_writer, which is tricky.
|
||||
// Simpler to pass the writer or print directly for now.
|
||||
// Let's redefine prt_ast slightly for main.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Redefine prt_ast locally or pass writer - let's redefine for simplicity here
|
||||
fn print_ast_to_writer<W: Write>(writer: &mut W, node: Option<&AstNode>, indent: usize) -> io::Result<()> {
|
||||
let prefix = " ".repeat(indent);
|
||||
match node {
|
||||
None => writeln!(writer, "{}<null>", prefix),
|
||||
Some(t) => {
|
||||
let node_name = DISPLAY_NODES.get(t.node_type as usize).unwrap_or(&"UnknownNode");
|
||||
match t.node_type {
|
||||
NodeType::Ident | NodeType::Integer | NodeType::String => {
|
||||
writeln!(writer, "{}{:<14} {}", prefix, node_name, t.value.as_deref().unwrap_or(""))?;
|
||||
}
|
||||
_ => {
|
||||
writeln!(writer, "{}{:<14}", prefix, node_name)?;
|
||||
print_ast_to_writer(writer, t.left.as_deref(), indent + 1)?;
|
||||
print_ast_to_writer(writer, t.right.as_deref(), indent + 1)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Call the modified print function
|
||||
print_ast_to_writer(&mut buf_writer, Some(&ast_root), 0)
|
||||
.map_err(|e| format!("Error writing AST: {}", e))?;
|
||||
|
||||
|
||||
// Ensure output buffer is flushed
|
||||
buf_writer.flush()?;
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Parse Error: {}", e); // Print error to stderr
|
||||
exit(1); // Exit with non-zero status on error
|
||||
}
|
||||
}
|
||||
|
||||
Ok(()) // Indicate success
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue