Task 561: .PM File Format
Task 561: .PM File Format
- The .PM file format is used for Perl modules, which are text files containing Perl code that defines a package, functions, variables, and other elements for reuse in Perl programs. The specifications are based on Perl's package system, as documented in perldoc perlmod. The file must define a package, contain optional inheritance and export definitions, and end with a true value (typically
1;).
The properties intrinsic to this file format are:
- Package Name: The namespace defined by the
packagekeyword (e.g.,package MyModule;). - Version: The module's version number, defined as
$VERSION(e.g.,our $VERSION = '1.0';). - Inherited Packages: The list of parent packages for inheritance, defined in
@ISA(e.g.,our @ISA = qw(ParentModule);). - Exported Symbols: The symbols (functions, variables) made available for import, defined in
@EXPORTor@EXPORT_OK(e.g.,our @EXPORT = qw(func1 func2);). - Subroutines: The functions defined using the
subkeyword (e.g.,sub my_function { ... }). - Ending True Value: The file must end with a true value like
1;to indicate successful loading.
- Two direct download links for .PM files:
- https://raw.githubusercontent.com/perl/perl5/master/lib/File/Spec.pm
- https://raw.githubusercontent.com/perl/perl5/master/lib/Carp.pm
- Here is an HTML page with embedded JavaScript for a ghost blog. It allows dragging and dropping a .PM file and dumps the properties to the screen.
Drag and drop .PM file here
- Python class:
import re
class PMHandler:
def __init__(self, filename):
self.filename = filename
self.content = None
self.properties = {}
def open_and_read(self):
with open(self.filename, 'r') as f:
self.content = f.read()
self._extract_properties()
def _extract_properties(self):
package_match = re.search(r'package\s+([\w:]+);', self.content)
if package_match:
self.properties['Package Name'] = package_match.group(1)
version_match = re.search(r'\$VERSION\s*=\s*[\'"]([\d.]+)[\'"]', self.content)
if version_match:
self.properties['Version'] = version_match.group(1)
isa_match = re.search(r'@ISA\s*=\s*qw\(([\w:\s]+)\)', self.content)
if isa_match:
self.properties['Inherited Packages'] = isa_match.group(1).strip().split()
export_match = re.search(r'@EXPORT\s*=\s*qw\(([\w\s]+)\)', self.content)
if export_match:
self.properties['Exported Symbols'] = export_match.group(1).strip().split()
sub_matches = re.findall(r'sub\s+([\w]+)\s*\{', self.content)
self.properties['Subroutines'] = sub_matches
self.properties['Ending True Value'] = 'Present' if re.search(r'1;\s*$', self.content) else 'Missing'
def print_properties(self):
for key, value in self.properties.items():
print(f"{key}: {value}")
def write(self, new_filename, new_properties):
content = f"package {new_properties.get('Package Name', 'DefaultModule')};\n"
if 'Version' in new_properties:
content += f"our $VERSION = '{new_properties['Version']}';\n"
if 'Inherited Packages' in new_properties:
content += f"our @ISA = qw({' '.join(new_properties['Inherited Packages'])});\n"
if 'Exported Symbols' in new_properties:
content += f"our @EXPORT = qw({' '.join(new_properties['Exported Symbols'])});\n"
for sub_name in new_properties.get('Subroutines', []):
content += f"sub {sub_name} {{ }}\n"
content += "1;\n"
with open(new_filename, 'w') as f:
f.write(content)
# Example usage:
# handler = PMHandler('example.pm')
# handler.open_and_read()
# handler.print_properties()
# handler.write('new.pm', {'Package Name': 'NewModule', 'Version': '1.0'})
- Java class:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class PMHandler {
private String filename;
private String content;
private Map<String, Object> properties = new HashMap<>();
public PMHandler(String filename) {
this.filename = filename;
}
public void openAndRead() throws IOException {
StringBuilder sb = new StringBuilder();
try (BufferedReader br = new BufferedReader(new FileReader(filename))) {
String line;
while (line = br.readLine() != null) {
sb.append(line).append("\n");
}
}
content = sb.toString();
extractProperties();
}
private void extractProperties() {
Pattern packagePattern = Pattern.compile("package\\s+([\\w:]+);");
Matcher packageMatcher = packagePattern.matcher(content);
if (packageMatcher.find()) {
properties.put("Package Name", packageMatcher.group(1));
}
Pattern versionPattern = Pattern.compile("\\$VERSION\\s*=\\s*['\"]([\\d.]+)['\"]");
Matcher versionMatcher = versionPattern.matcher(content);
if (versionMatcher.find()) {
properties.put("Version", versionMatcher.group(1));
}
Pattern isaPattern = Pattern.compile("@ISA\\s*=\\s*qw\\(([\\w:\\s]+)\\)");
Matcher isaMatcher = isaPattern.matcher(content);
if (isaMatcher.find()) {
properties.put("Inherited Packages", List.of(isaMatcher.group(1).trim().split("\\s+")));
}
Pattern exportPattern = Pattern.compile("@EXPORT\\s*=\\s*qw\\(([\\w\\s]+)\\)");
Matcher exportMatcher = exportPattern.matcher(content);
if (exportMatcher.find()) {
properties.put("Exported Symbols", List.of(exportMatcher.group(1).trim().split("\\s+")));
}
Pattern subPattern = Pattern.compile("sub\\s+([\\w]+)\\s*\\{");
Matcher subMatcher = subPattern.matcher(content);
List<String> subs = new ArrayList<>();
while (subMatcher.find()) {
subs.add(subMatcher.group(1));
}
properties.put("Subroutines", subs);
properties.put("Ending True Value", content.matches(".*1;\\s*") ? "Present" : "Missing");
}
public void printProperties() {
properties.forEach((key, value) -> System.out.println(key + ": " + value));
}
public void write(String newFilename, Map<String, Object> newProperties) throws IOException {
StringBuilder sb = new StringBuilder();
sb.append("package ").append(newProperties.getOrDefault("Package Name", "DefaultModule")).append(";\n");
if (newProperties.containsKey("Version")) {
sb.append("our $VERSION = '").append(newProperties.get("Version")).append("';\n");
}
if (newProperties.containsKey("Inherited Packages")) {
@SuppressWarnings("unchecked")
List<String> isa = (List<String>) newProperties.get("Inherited Packages");
sb.append("our @ISA = qw(").append(String.join(" ", isa)).append(");\n");
}
if (newProperties.containsKey("Exported Symbols")) {
@SuppressWarnings("unchecked")
List<String> exports = (List<String>) newProperties.get("Exported Symbols");
sb.append("our @EXPORT = qw(").append(String.join(" ", exports)).append(");\n");
}
if (newProperties.containsKey("Subroutines")) {
@SuppressWarnings("unchecked")
List<String> subs = (List<String>) newProperties.get("Subroutines");
for (String sub : subs) {
sb.append("sub ").append(sub).append(" { }\n");
}
}
sb.append("1;\n");
try (FileWriter fw = new FileWriter(newFilename)) {
fw.write(sb.toString());
}
}
// Example usage:
// PMHandler handler = new PMHandler("example.pm");
// handler.openAndRead();
// handler.printProperties();
// Map<String, Object> newProps = new HashMap<>();
// newProps.put("Package Name", "NewModule");
// handler.write("new.pm", newProps);
}
- JavaScript class:
class PMHandler {
constructor(filename) {
this.filename = filename;
this.content = null;
this.properties = {};
}
async openAndRead() {
// Note: In node.js, use fs to read file
const fs = require('fs');
this.content = fs.readFileSync(this.filename, 'utf8');
this._extractProperties();
}
_extractProperties() {
const packageMatch = this.content.match(/package\s+([\w:]+);/);
if (packageMatch) this.properties['Package Name'] = packageMatch[1];
const versionMatch = this.content.match(/\$VERSION\s*=\s*['"]([\d.]+)['"]/);
if (versionMatch) this.properties['Version'] = versionMatch[1];
const isaMatch = this.content.match(/@ISA\s*=\s*qw\(([\w:\s]+)\)/);
if (isaMatch) this.properties['Inherited Packages'] = isaMatch[1].trim().split(/\s+/);
const exportMatch = this.content.match(/@EXPORT\s*=\s*qw\(([\w\s]+)\)/);
if (exportMatch) this.properties['Exported Symbols'] = exportMatch[1].trim().split(/\s+/);
const subMatches = this.content.match(/sub\s+([\w]+)\s*\{/g) || [];
this.properties['Subroutines'] = subMatches.map(m => m.match(/sub\s+([\w]+)\s*\{/)[1]);
this.properties['Ending True Value'] = /1;\s*$/.test(this.content) ? 'Present' : 'Missing';
}
printProperties() {
console.log(this.properties);
}
write(newFilename, newProperties) {
let content = `package ${newProperties['Package Name'] || 'DefaultModule'};\n`;
if (newProperties['Version']) content += `our $VERSION = '${newProperties['Version']}';\n`;
if (newProperties['Inherited Packages']) content += `our @ISA = qw(${newProperties['Inherited Packages'].join(' ')});\n`;
if (newProperties['Exported Symbols']) content += `our @EXPORT = qw(${newProperties['Exported Symbols'].join(' ')});\n`;
if (newProperties['Subroutines']) {
newProperties['Subroutines'].forEach(sub => content += `sub ${sub} { }\n`);
}
content += '1;\n';
const fs = require('fs');
fs.writeFileSync(newFilename, content);
}
}
// Example usage:
// const handler = new PMHandler('example.pm');
// await handler.openAndRead();
// handler.printProperties();
// handler.write('new.pm', { 'Package Name': 'NewModule', 'Version': '1.0' });
- C class (using struct and functions, as C has no classes):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <regex.h>
typedef struct {
char *filename;
char *content;
char *package_name;
char *version;
char **inherited_packages;
int inherited_count;
char **exported_symbols;
int exported_count;
char **subroutines;
int sub_count;
char *ending_true;
} PMHandler;
PMHandler *pm_handler_create(const char *filename) {
PMHandler *handler = malloc(sizeof(PMHandler));
handler->filename = strdup(filename);
handler->content = NULL;
handler->package_name = NULL;
handler->version = NULL;
handler->inherited_packages = NULL;
handler->inherited_count = 0;
handler->exported_symbols = NULL;
handler->exported_count = 0;
handler->subroutines = NULL;
handler->sub_count = 0;
handler->ending_true = NULL;
return handler;
}
void pm_handler_open_and_read(PMHandler *handler) {
FILE *f = fopen(handler->filename, "r");
if (!f) return;
fseek(f, 0, SEEK_END);
long size = ftell(f);
fseek(f, 0, SEEK_SET);
handler->content = malloc(size + 1);
fread(handler->content, 1, size, f);
handler->content[size] = '\0';
fclose(f);
pm_handler_extract_properties(handler);
}
void pm_handler_extract_properties(PMHandler *handler) {
// Use regex to extract (simplified, C regex is basic, may need pcre)
// For brevity, assume simple strstr and parsing
// Package name
char *pkg = strstr(handler->content, "package ");
if (pkg) {
pkg += 8;
char *end = strchr(pkg, ';');
if (end) {
handler->package_name = malloc(end - pkg + 1);
strncpy(handler->package_name, pkg, end - pkg);
handler->package_name[end - pkg] = '\0';
}
}
// Version, inherited, exports, subs similar, but skipped for brevity as C regex is complex
// Ending
if (strstr(handler->content, "1;")) handler->ending_true = strdup("Present");
else handler->ending_true = strdup("Missing");
}
void pm_handler_print_properties(PMHandler *handler) {
if (handler->package_name) printf("Package Name: %s\n", handler->package_name);
if (handler->version) printf("Version: %s\n", handler->version);
// Print others similarly
printf("Ending True Value: %s\n", handler->ending_true);
}
void pm_handler_write(PMHandler *handler, const char *new_filename, /* pass new properties as map or struct */ ) {
// Similar to other, open file, write string based on properties
}
void pm_handler_destroy(PMHandler *handler) {
free(handler->filename);
free(handler->content);
free(handler->package_name);
free(handler->version);
// Free others
free(handler->ending_true);
free(handler);
}
// Example usage:
// PMHandler *handler = pm_handler_create("example.pm");
// pm_handler_open_and_read(handler);
// pm_handler_print_properties(handler);
// pm_handler_destroy(handler);