001// Copyright (c) FIRST and other WPILib contributors. 002// Open Source Software; you can modify and/or share it under the terms of 003// the WPILib BSD license file in the root directory of this project. 004 005package edu.wpi.first.util.struct.parser; 006 007import java.util.HashMap; 008import java.util.Map; 009 010/** Raw struct schema parser. */ 011public class Parser { 012 /** 013 * Construct a raw struct schema parser. 014 * 015 * @param in schema 016 */ 017 public Parser(String in) { 018 m_lexer = new Lexer(in); 019 } 020 021 /** 022 * Parses the schema. 023 * 024 * @return parsed schema object 025 * @throws ParseException on parse error 026 */ 027 public ParsedSchema parse() throws ParseException { 028 ParsedSchema schema = new ParsedSchema(); 029 do { 030 getNextToken(); 031 if (m_token == TokenKind.kSemicolon) { 032 continue; 033 } 034 if (m_token == TokenKind.kEndOfInput) { 035 break; 036 } 037 schema.declarations.add(parseDeclaration()); 038 } while (m_token != TokenKind.kEndOfInput); 039 return schema; 040 } 041 042 private ParsedDeclaration parseDeclaration() throws ParseException { 043 ParsedDeclaration decl = new ParsedDeclaration(); 044 045 // optional enum specification 046 if (m_token == TokenKind.kIdentifier && "enum".equals(m_lexer.getTokenText())) { 047 getNextToken(); 048 expect(TokenKind.kLeftBrace); 049 decl.enumValues = parseEnum(); 050 getNextToken(); 051 } else if (m_token == TokenKind.kLeftBrace) { 052 decl.enumValues = parseEnum(); 053 getNextToken(); 054 } 055 056 // type name 057 expect(TokenKind.kIdentifier); 058 decl.typeString = m_lexer.getTokenText(); 059 getNextToken(); 060 061 // identifier name 062 expect(TokenKind.kIdentifier); 063 decl.name = m_lexer.getTokenText(); 064 getNextToken(); 065 066 // array or bit field 067 if (m_token == TokenKind.kLeftBracket) { 068 getNextToken(); 069 expect(TokenKind.kInteger); 070 String valueStr = m_lexer.getTokenText(); 071 int value; 072 try { 073 value = Integer.parseInt(valueStr); 074 } catch (NumberFormatException e) { 075 value = 0; 076 } 077 if (value > 0) { 078 decl.arraySize = value; 079 } else { 080 throw new ParseException( 081 m_lexer.m_pos, "array size '" + valueStr + "' is not a positive integer"); 082 } 083 getNextToken(); 084 expect(TokenKind.kRightBracket); 085 getNextToken(); 086 } else if (m_token == TokenKind.kColon) { 087 getNextToken(); 088 expect(TokenKind.kInteger); 089 String valueStr = m_lexer.getTokenText(); 090 int value; 091 try { 092 value = Integer.parseInt(valueStr); 093 } catch (NumberFormatException e) { 094 value = 0; 095 } 096 if (value > 0) { 097 decl.bitWidth = value; 098 } else { 099 throw new ParseException( 100 m_lexer.m_pos, "bitfield width '" + valueStr + "' is not a positive integer"); 101 } 102 getNextToken(); 103 } 104 105 // declaration must end with EOF or semicolon 106 if (m_token != TokenKind.kEndOfInput) { 107 expect(TokenKind.kSemicolon); 108 } 109 110 return decl; 111 } 112 113 private Map<String, Long> parseEnum() throws ParseException { 114 Map<String, Long> map = new HashMap<>(); 115 116 // we start with current = '{' 117 getNextToken(); 118 while (m_token != TokenKind.kRightBrace) { 119 expect(TokenKind.kIdentifier); 120 final String name = m_lexer.getTokenText(); 121 getNextToken(); 122 expect(TokenKind.kEquals); 123 getNextToken(); 124 expect(TokenKind.kInteger); 125 String valueStr = m_lexer.getTokenText(); 126 long value; 127 try { 128 value = Long.parseLong(valueStr); 129 } catch (NumberFormatException e) { 130 throw new ParseException(m_lexer.m_pos, "could not parse enum value '" + valueStr + "'"); 131 } 132 map.put(name, value); 133 getNextToken(); 134 if (m_token == TokenKind.kRightBrace) { 135 break; 136 } 137 expect(TokenKind.kComma); 138 getNextToken(); 139 } 140 return map; 141 } 142 143 private TokenKind getNextToken() { 144 m_token = m_lexer.scan(); 145 return m_token; 146 } 147 148 private void expect(TokenKind kind) throws ParseException { 149 if (m_token != kind) { 150 throw new ParseException( 151 m_lexer.m_pos, "expected " + kind + ", got '" + m_lexer.getTokenText() + "'"); 152 } 153 } 154 155 final Lexer m_lexer; 156 TokenKind m_token; 157}