diff --git a/src/app/registers/RegisterBrowser.tsx b/src/app/registers/RegisterBrowser.tsx index baaaac6..307c5dc 100644 --- a/src/app/registers/RegisterBrowser.tsx +++ b/src/app/registers/RegisterBrowser.tsx @@ -1,7 +1,7 @@ 'use client'; import { useState } from 'react'; -import { Register, RegisterAccess, Note } from './types'; +import { Register, RegisterAccess, Note } from '@/utils/parser'; import { Form, Container, Row, Table, OverlayTrigger, Tooltip } from 'react-bootstrap'; import RegisterDetail from "@/app/registers/RegisterDetail"; diff --git a/src/app/registers/RegisterDetail.tsx b/src/app/registers/RegisterDetail.tsx index 9a06e13..07b4c6e 100644 --- a/src/app/registers/RegisterDetail.tsx +++ b/src/app/registers/RegisterDetail.tsx @@ -1,7 +1,7 @@ "use client"; import { Col, Card, Tabs, Tab } from 'react-bootstrap'; -import { Register } from './types'; +import { Register } from '@/utils/parser'; import { renderAccess } from './RegisterBrowser'; import Link from "next/link"; import * as Icon from 'react-bootstrap-icons'; diff --git a/src/app/registers/[hex]/page.tsx b/src/app/registers/[hex]/page.tsx index 9bb8d88..eecc078 100644 --- a/src/app/registers/[hex]/page.tsx +++ b/src/app/registers/[hex]/page.tsx @@ -1,170 +1,12 @@ -import { promises as fs } from 'fs'; -import path from 'path'; import { notFound } from 'next/navigation'; import Link from 'next/link'; -import { Register, RegisterAccess } from '@/app/registers/types'; +import { Register } from '@/utils/parser'; import RegisterDetail from '@/app/registers/RegisterDetail'; import {Container, Row} from "react-bootstrap"; - -async function parseNextReg(fileContent: string): Promise { - const registers: Register[] = []; - const paragraphs = fileContent.split(/\n\s*\n/); - - for (const paragraph of paragraphs) { - if (!paragraph.trim()) { - continue; - } - processRegisterBlock(paragraph, registers); - } - - return registers; -} - -function processRegisterBlock(paragraph: string, registers: Register[]) { - const lines = paragraph.trim().split('\n'); - const firstLine = lines[0]; - - const registerMatch = firstLine.match(/([0-9a-fA-F,x]+)\s*\((.*?)\)\s*=>\s*(.*)/); - - if (!registerMatch) { - return; - } - - const hexAddresses = registerMatch[1].trim(); - const decAddresses = registerMatch[2].trim(); - const name = registerMatch[3] ? registerMatch[3].trim() : ''; - const description = lines.slice(1).join('\n').trim(); - - const hexList = hexAddresses.split(',').map(h => h.trim()); - const decList = decAddresses.includes('-') ? decAddresses.split('-') : decAddresses.split(',').map(d => d.trim()); - - const createRegister = (hex: string, dec: string | number, regName: string): Register => { - const reg: Register = { - hex_address: hex, - dec_address: dec, - name: regName, - description: description, - notes: [], - text: "", - issue_4_only: false - }; - - const descriptionLines = description.split('\n'); - let currentAccess: 'read' | 'write' | 'common' | null = null; - let accessData: RegisterAccess = { operations: [], notes: [] }; - - for (const line of descriptionLines) { - if(line.includes('Issue 4 Only')) reg.issue_4_only = true; - - const trimmedLine = line.trim(); - - if (trimmedLine.startsWith('//')) continue; - - if (trimmedLine.startsWith('(R)')) { - if (currentAccess) reg[currentAccess] = accessData; - accessData = { operations: [], notes: [] }; - currentAccess = 'read'; - continue; - } - if (trimmedLine.startsWith('(W)')) { - if (currentAccess) reg[currentAccess] = accessData; - accessData = { operations: [], notes: [] }; - currentAccess = 'write'; - continue; - } - if (trimmedLine.startsWith('(R/W')) { - if (currentAccess) reg[currentAccess] = accessData; - accessData = { operations: [], notes: [] }; - currentAccess = 'common'; - continue; - } - if (line.startsWith(trimmedLine)) { - if (currentAccess) reg[currentAccess] = accessData; - accessData = { operations: [], notes: [] }; - currentAccess = null; - } - - if (currentAccess) { - const bitMatch = trimmedLine.match(/^(bits?|bit)\s+([\d:-]+)\s*=\s*(.*)/); - const valueMatch = !line.match(/^\s+/) && trimmedLine.match(/^([01\s]+)\s*=\s*(.*)/); - - if (bitMatch) { - let bitDescription = bitMatch[3]; - const footnoteMatch = bitDescription.match(/(\*+)$/); - let footnoteRef: string | undefined = undefined; - if (footnoteMatch) { - footnoteRef = footnoteMatch[1]; - bitDescription = bitDescription.substring(0, bitDescription.length - footnoteRef.length).trim(); - } - accessData.operations.push({ - bits: bitMatch[2], - description: bitDescription, - footnoteRef: footnoteRef, - }); - } else if (valueMatch) { - accessData.operations.push({ - bits: valueMatch[1].trim().replace(/\s/g, ''), - description: valueMatch[2].trim(), - }); - } else if (trimmedLine.startsWith('*')) { - const noteMatch = trimmedLine.match(/^(\*+)\s*(.*)/); - if (noteMatch) { - accessData.notes.push({ - ref: noteMatch[1], - text: noteMatch[2], - }); - } - } else if (trimmedLine) { - if (line.match(/^\s+/) && accessData.operations.length > 0) { - accessData.operations[accessData.operations.length - 1].description += `\n${line}`; - } else { - - if (!accessData.description) { - accessData.description = ''; - } - accessData.description += `\n${trimmedLine}`; - } - } - } else { - if (trimmedLine.startsWith('*')) { - const noteMatch = trimmedLine.match(/^(\*+)\s*(.*)/); - if (noteMatch) { - (reg as Register).notes.push({ - ref: noteMatch[1], - text: noteMatch[2], - }); - } - } - else { - reg.text += `${line}\n`; - } - } - } - if (currentAccess) { - (reg as Register)[currentAccess] = accessData; - } - - return reg; - }; - - if (hexList.length > 1) { - for (let i = 0; i < hexList.length; i++) { - const hexAddr = hexList[i]; - const decAddr = (decList as string[])[i] || decAddresses; - const dec = isNaN(parseInt(decAddr, 10)) ? (decAddr) : parseInt(decAddr, 10); - registers.push(createRegister(hexAddr, dec, `${name} (${hexAddr})`)); - } - } else { - const dec = isNaN(parseInt(decAddresses, 10)) ? (decAddresses) : parseInt(decAddresses, 10); - registers.push(createRegister(hexAddresses, dec, name)); - } -} +import { getRegisters } from '@/app/services/register.service'; export default async function RegisterDetailPage({ params }: { params: { hex: string } }) { - const filePath = path.join(process.cwd(), 'data', 'nextreg.txt'); - const fileContent = await fs.readFile(filePath, 'utf8'); - const registers = await parseNextReg(fileContent); - + const registers = await getRegisters(); const targetHex = decodeURIComponent((await params).hex).toLowerCase(); const register = registers.find(r => r.hex_address.toLowerCase() === targetHex); diff --git a/src/app/registers/page.tsx b/src/app/registers/page.tsx index 4479fe0..10af6ad 100644 --- a/src/app/registers/page.tsx +++ b/src/app/registers/page.tsx @@ -1,174 +1,8 @@ -import { promises as fs } from 'fs'; -import path from 'path'; import RegisterBrowser from '@/app/registers/RegisterBrowser'; -import { Register, RegisterAccess } from './types'; - -/** - * Parses the content of the nextreg.txt file and returns an array of register objects. - * @param fileContent The content of the nextreg.txt file. - * @returns A promise that resolves to an array of Register objects. - */ -async function parseNextReg(fileContent: string): Promise { - const registers: Register[] = []; - const paragraphs = fileContent.split(/\n\s*\n/); - - for (const paragraph of paragraphs) { - if (!paragraph.trim()) { - continue; - } - processRegisterBlock(paragraph, registers); - } - - return registers; -} - -function processRegisterBlock(paragraph: string, registers: Register[]) { - const lines = paragraph.trim().split('\n'); - const firstLine = lines[0]; - - const registerMatch = firstLine.match(/([0-9a-fA-F,x]+)\s*\((.*?)\)\s*=>\s*(.*)/); - - if (!registerMatch) { - return; - } - - const hexAddresses = registerMatch[1].trim(); - const decAddresses = registerMatch[2].trim(); - const name = registerMatch[3] ? registerMatch[3].trim() : ''; - const description = lines.slice(1).join('\n').trim(); - - const hexList = hexAddresses.split(',').map(h => h.trim()); - const decList = decAddresses.includes('-') ? decAddresses.split('-') : decAddresses.split(',').map(d => d.trim()); - - const createRegister = (hex: string, dec: string | number, regName: string): Register => { - const reg: Register = { - hex_address: hex, - dec_address: dec, - name: regName, - description: description, - notes: [], - text: "", - issue_4_only: false - }; - - const descriptionLines = description.split('\n'); - let currentAccess: 'read' | 'write' | 'common' | null = null; - let accessData: RegisterAccess = { operations: [], notes: [] }; - - for (const line of descriptionLines) { - if(line.includes('Issue 4 Only')) reg.issue_4_only = true; - - const trimmedLine = line.trim(); - - if (trimmedLine.startsWith('//')) continue; - - if (trimmedLine.startsWith('(R)')) { - if (currentAccess) reg[currentAccess] = accessData; - accessData = { operations: [], notes: [] }; - currentAccess = 'read'; - continue; - } - if (trimmedLine.startsWith('(W)')) { - if (currentAccess) reg[currentAccess] = accessData; - accessData = { operations: [], notes: [] }; - currentAccess = 'write'; - continue; - } - if (trimmedLine.startsWith('(R/W')) { - if (currentAccess) reg[currentAccess] = accessData; - accessData = { operations: [], notes: [] }; - currentAccess = 'common'; - continue; - } - if (line.startsWith(trimmedLine)) { - if (currentAccess) reg[currentAccess] = accessData; - accessData = { operations: [], notes: [] }; - currentAccess = null; - } - - if (currentAccess) { - const bitMatch = trimmedLine.match(/^(bits?|bit)\s+([\d:-]+)\s*=\s*(.*)/); - const valueMatch = !line.match(/^\s+/) && trimmedLine.match(/^([01\s]+)\s*=\s*(.*)/); - - if (bitMatch) { - let bitDescription = bitMatch[3]; - const footnoteMatch = bitDescription.match(/(\*+)$/); - let footnoteRef: string | undefined = undefined; - if (footnoteMatch) { - footnoteRef = footnoteMatch[1]; - bitDescription = bitDescription.substring(0, bitDescription.length - footnoteRef.length).trim(); - } - accessData.operations.push({ - bits: bitMatch[2], - description: bitDescription, - footnoteRef: footnoteRef, - }); - } else if (valueMatch) { - accessData.operations.push({ - bits: valueMatch[1].trim().replace(/\s/g, ''), - description: valueMatch[2].trim(), - }); - } else if (trimmedLine.startsWith('*')) { - const noteMatch = trimmedLine.match(/^(\*+)\s*(.*)/); - if (noteMatch) { - accessData.notes.push({ - ref: noteMatch[1], - text: noteMatch[2], - }); - } - } else if (trimmedLine) { - if (line.match(/^\s+/) && accessData.operations.length > 0) { - accessData.operations[accessData.operations.length - 1].description += `\n${line}`; - } else { - - if (!accessData.description) { - accessData.description = ''; - } - accessData.description += `\n${trimmedLine}`; - } - } - } else { - if (trimmedLine.startsWith('*')) { - const noteMatch = trimmedLine.match(/^(\*+)\s*(.*)/); - if (noteMatch) { - reg.notes.push({ - ref: noteMatch[1], - text: noteMatch[2], - }); - } - } - else { - reg.text += `${line}\n`; - } - } - } - if (currentAccess) { - reg[currentAccess] = accessData; - } - - return reg; - }; - - if (hexList.length > 1) { - for (let i = 0; i < hexList.length; i++) { - const hexAddr = hexList[i]; - const decAddr = decList[i] || decAddresses; - const dec = isNaN(parseInt(decAddr, 10)) ? decAddr : parseInt(decAddr, 10); - registers.push(createRegister(hexAddr, dec, `${name} (${hexAddr})`)); - } - } else { - const dec = isNaN(parseInt(decAddresses, 10)) ? decAddresses : parseInt(decAddresses, 10); - registers.push(createRegister(hexAddresses, dec, name)); - } -} - - - +import { getRegisters } from '@/app/services/register.service'; export default async function RegistersPage() { - const filePath = path.join(process.cwd(), 'data', 'nextreg.txt'); - const fileContent = await fs.readFile(filePath, 'utf8'); - const registers = await parseNextReg(fileContent); + const registers = await getRegisters(); return (
diff --git a/src/app/registers/types.ts b/src/app/registers/types.ts deleted file mode 100644 index 5ff646a..0000000 --- a/src/app/registers/types.ts +++ /dev/null @@ -1,29 +0,0 @@ - export interface BitwiseOperation { - bits: string; - description: string; - value?: string; - footnoteRef?: string; - } - - export interface Note { - ref: string; - text: string; - } - - export interface RegisterAccess { - description?: string; - operations: BitwiseOperation[]; - notes: Note[]; - } - export interface Register { - hex_address: string; - dec_address: number | string; - name: string; - description: string; - read?: RegisterAccess; - write?: RegisterAccess; - common?: RegisterAccess; - text: string; - notes: Note[]; - issue_4_only: boolean; - } diff --git a/src/app/services/register.service.ts b/src/app/services/register.service.ts new file mode 100644 index 0000000..2a93df9 --- /dev/null +++ b/src/app/services/register.service.ts @@ -0,0 +1,20 @@ + +import { promises as fs } from 'fs'; +import path from 'path'; +import { Register } from '@/utils/parser'; +import { parseNextReg } from '@/utils/parser'; + +let registers: Register[] = []; + +/** + * Gets the registers from the in-memory cache, or loads them from the file if not already loaded. + * @returns A promise that resolves to an array of Register objects. + */ +export async function getRegisters(): Promise { + if (registers.length === 0) { + const filePath = path.join(process.cwd(), 'data', 'nextreg.txt'); + const fileContent = await fs.readFile(filePath, 'utf8'); + registers = await parseNextReg(fileContent); + } + return registers; +} diff --git a/src/utils/parser.ts b/src/utils/parser.ts new file mode 100644 index 0000000..7db9b4f --- /dev/null +++ b/src/utils/parser.ts @@ -0,0 +1,188 @@ +export interface BitwiseOperation { + bits: string; + description: string; + value?: string; + footnoteRef?: string; +} + +export interface Note { + ref: string; + text: string; +} + +export interface RegisterAccess { + description?: string; + operations: BitwiseOperation[]; + notes: Note[]; +} +export interface Register { + hex_address: string; + dec_address: number | string; + name: string; + description: string; + read?: RegisterAccess; + write?: RegisterAccess; + common?: RegisterAccess; + text: string; + notes: Note[]; + issue_4_only: boolean; +} + +/** + * Parses the content of the nextreg.txt file and returns an array of register objects. + * @param fileContent The content of the nextreg.txt file. + * @returns A promise that resolves to an array of Register objects. + */ +export async function parseNextReg(fileContent: string): Promise { + const registers: Register[] = []; + const paragraphs = fileContent.split(/\n\s*\n/); + + for (const paragraph of paragraphs) { + if (!paragraph.trim()) { + continue; + } + processRegisterBlock(paragraph, registers); + } + + return registers; +} + +export function processRegisterBlock(paragraph: string, registers: Register[]) { + const lines = paragraph.trim().split('\n'); + const firstLine = lines[0]; + + const registerMatch = firstLine.match(/([0-9a-fA-F,x]+)\s*\((.*?)\)\s*=>\s*(.*)/); + + if (!registerMatch) { + return; + } + + const hexAddresses = registerMatch[1].trim(); + const decAddresses = registerMatch[2].trim(); + const name = registerMatch[3] ? registerMatch[3].trim() : ''; + const description = lines.slice(1).join('\n').trim(); + + const hexList = hexAddresses.split(',').map(h => h.trim()); + const decList = decAddresses.includes('-') ? decAddresses.split('-') : decAddresses.split(',').map(d => d.trim()); + + const createRegister = (hex: string, dec: string | number, regName: string): Register => { + const reg: Register = { + hex_address: hex, + dec_address: dec, + name: regName, + description: description, + notes: [], + text: "", + issue_4_only: false + }; + + const descriptionLines = description.split('\n'); + let currentAccess: 'read' | 'write' | 'common' | null = null; + let accessData: RegisterAccess = { operations: [], notes: [] }; + + for (const line of descriptionLines) { + if(line.includes('Issue 4 Only')) reg.issue_4_only = true; + + const trimmedLine = line.trim(); + + if (trimmedLine.startsWith('//')) continue; + + if (trimmedLine.startsWith('(R)')) { + if (currentAccess) reg[currentAccess] = accessData; + accessData = { operations: [], notes: [] }; + currentAccess = 'read'; + continue; + } + if (trimmedLine.startsWith('(W)')) { + if (currentAccess) reg[currentAccess] = accessData; + accessData = { operations: [], notes: [] }; + currentAccess = 'write'; + continue; + } + if (trimmedLine.startsWith('(R/W')) { + if (currentAccess) reg[currentAccess] = accessData; + accessData = { operations: [], notes: [] }; + currentAccess = 'common'; + continue; + } + if (line.startsWith(trimmedLine)) { + if (currentAccess) reg[currentAccess] = accessData; + accessData = { operations: [], notes: [] }; + currentAccess = null; + } + + if (currentAccess) { + const bitMatch = trimmedLine.match(/^(bits?|bit)\s+([\d:-]+)\s*=\s*(.*)/); + const valueMatch = !line.match(/^\s+/) && trimmedLine.match(/^([01\s]+)\s*=\s*(.*)/); + + if (bitMatch) { + let bitDescription = bitMatch[3]; + const footnoteMatch = bitDescription.match(/(\*+)$/); + let footnoteRef: string | undefined = undefined; + if (footnoteMatch) { + footnoteRef = footnoteMatch[1]; + bitDescription = bitDescription.substring(0, bitDescription.length - footnoteRef.length).trim(); + } + accessData.operations.push({ + bits: bitMatch[2], + description: bitDescription, + footnoteRef: footnoteRef, + }); + } else if (valueMatch) { + accessData.operations.push({ + bits: valueMatch[1].trim().replace(/\s/g, ''), + description: valueMatch[2].trim(), + }); + } else if (trimmedLine.startsWith('*')) { + const noteMatch = trimmedLine.match(/^(\*+)\s*(.*)/); + if (noteMatch) { + accessData.notes.push({ + ref: noteMatch[1], + text: noteMatch[2], + }); + } + } else if (trimmedLine) { + if (line.match(/^\s+/) && accessData.operations.length > 0) { + accessData.operations[accessData.operations.length - 1].description += `\n${line}`; + } else { + + if (!accessData.description) { + accessData.description = ''; + } + accessData.description += `\n${trimmedLine}`; + } + } + } else { + if (trimmedLine.startsWith('*')) { + const noteMatch = trimmedLine.match(/^(\*+)\s*(.*)/); + if (noteMatch) { + reg.notes.push({ + ref: noteMatch[1], + text: noteMatch[2], + }); + } + } + else { + reg.text += `${line}\n`; + } + } + } + if (currentAccess) { + reg[currentAccess] = accessData; + } + + return reg; + }; + + if (hexList.length > 1) { + for (let i = 0; i < hexList.length; i++) { + const hexAddr = hexList[i]; + const decAddr = decList[i] || decAddresses; + const dec = isNaN(parseInt(decAddr, 10)) ? decAddr : parseInt(decAddr, 10); + registers.push(createRegister(hexAddr, dec, `${name} (${hexAddr})`)); + } + } else { + const dec = isNaN(parseInt(decAddresses, 10)) ? decAddresses : parseInt(decAddresses, 10); + registers.push(createRegister(hexAddresses, dec, name)); + } +}