#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