// https://chatgpt.com // prompt: // write a grammar parser in zig; // choose a substantial grammar and specify it as a parameter to constructing the parser, // then use the parser to validate a substantial example of that grammar // grammar: // Expression ::= Term (('+' | '-') Term)* // Term ::= Factor (('*' | '/') Factor)* // Factor ::= Number | '(' Expression ')' // Number ::= [0-9]+ const std = @import( "std" ); const debug = std.debug.print; const Parser = struct { input :[]const u8, index :usize, fn parseWhitespace( self :*Parser ) void { const rem = self.input[ self.index.. ]; for( rem, self.index.. ) |char, i| { switch( char ) { ' ','\t','\n','\r' => continue, else => { self.index = i; return; } } } self.index = self.input.len; } fn parseNumber( self :*Parser ) ParseError!u32 { defer { self.parseWhitespace(); } // const index_before = Parser.index; var value :u32 = 0; var found :bool = false; while( self.index < self.input.len ) { const c = self.input[ self.index ]; if( '0' <= c and c <= '9' ) { found = true; value = value * 10 + ( c - '0' ); self.index += 1; } else { break; } } if( !found ) { return error.InvalidNumber; } // debug( "parseNumber: {d}\n", .{ value } ); // debug( "input: {s}\n", .{ self.input[ index_before .. self.index ] } ); // debug( "----", .{} ); return value; } fn parseFactor( self :*Parser ) ParseError!u32 { defer { self.parseWhitespace(); } // const index_before = self.index; // var x = error.UnexpectedEnd; // defer { // debug( "parseFactor: ", .{} ); // // const x = ( Parser { .input = self.input, .index = index_before } ).parseFactor(); // if( x ) |val| { // debug( "{d}\n", .{ val } ); // } else |e| { // debug( "{s}\n", .{ @errorName( e ) } ); // } // debug( "input: {s}\n", .{ self.input[ index_before..self.index ] } ); // } // if (self.index >= self.input.len) return error.UnexpectedEnd; const c = self.input[self.index]; if (c == '(') { self.index += 1; const result = try self.parseExpression(); if (self.index >= self.input.len or self.input[self.index] != ')') return error.MissingParenthesis; self.index += 1; // x = result; return result; } else if (c >= '0' and c <= '9') { return try self.parseNumber(); // x = try self.parseNumber(); // return x; } else { // x = error.UnexpectedCharacter; return error.UnexpectedCharacter; } } fn parseTerm(self :*Parser) ParseError!u32 { defer { self.parseWhitespace(); } var value = try self.parseFactor(); while (self.index < self.input.len) { const c = self.input[self.index]; if (c == '*' or c == '/') { self.index += 1; self.parseWhitespace(); const factor = try self.parseFactor(); if (c == '*') { value *= factor; } else { value /= factor; } } else { break; } } return value; } const ParseError = error { InvalidNumber, UnexpectedCharacter, MissingParenthesis, UnexpectedEnd, }; fn parseExpression(self :*Parser) ParseError!u32 { defer { self.parseWhitespace(); } var value = try self.parseTerm(); while( self.index < self.input.len ) { const c = self.input[ self.index ]; if( c == '+' or c == '-' ) { self.index += 1; self.parseWhitespace(); const term = try self.parseTerm(); if (c == '+') { value += term; } else { value -= term; } } else { break; } } return value; } fn parse( self :*Parser ) !u32 { self.parseWhitespace(); return try self.parseExpression(); } }; pub fn main() !void { // const allocator = std.heap.page_allocator; const input = "3 + (2 * 4) - 5"; var parser = Parser { .input = input, .index = 0, }; // const result = parser.parse(); // switch( result ) { // error.InvalidNumber => |e| debug( "{e}; Invalid number at index {}\n" , .{ @errorName(e), parser.index } ), // error.UnexpectedCharacter => |e| debug( "{e}; Unexpected character at index {}\n" , .{ @errorName(e), parser.index } ), // error.MissingParenthesis => |e| debug( "{e}; Missing closing parenthesis at index {}\n" , .{ @errorName(e), parser.index } ), // error.UnexpectedEnd => |e| debug( "{e}; Unexpected end of input\n" , .{ @errorName(e) } ), // else => |value| debug( "Parsed value: {}\n" , .{ value } ), // } if( parser.parse() ) |value| { debug( "Parsed value: {}\n", .{ value } ); } else |e| { debug( "{s}; ", .{ @errorName(e) } ); switch( e ) { error.InvalidNumber => debug( "Invalid number at index {}\n" , .{ parser.index } ), error.UnexpectedCharacter => debug( "Unexpected character at index {}\n" , .{ parser.index } ), error.MissingParenthesis => debug( "Missing closing parenthesis at index {}\n" , .{ parser.index } ), error.UnexpectedEnd => debug( "Unexpected end of input\n" , .{} ), } } }