#!/usr/bin/env python3
#####
# asy-list.py
#
# Build asy-keywords.el from a list of Asymptote global functions and
# variables. This script reads definitions from 'camp.l' and writes Emacs Lisp
# code to 'asy-keywords.el'.
#
#####
import argparse
import re
import textwrap
parser = argparse.ArgumentParser()
parser.add_argument(
"--asy-list-file", type=str, required=True, help="Path to the asy list file"
)
parser.add_argument("--revision", type=str, required=True, help="Revision identifier")
parser.add_argument(
"--output-file", type=str, required=True, help="Path to output file"
)
args = parser.parse_args()
# Open the file 'asy-keywords.el' for writing.
with open(args.output_file, "w", encoding="utf-8") as keywords:
# Write header information to 'asy-keywords.el'.
# This includes comments and the definition of 'asy-keywords-version' using a
# command-line argument.
keywords.write(
textwrap.dedent(
f"""\
;;
;; This file is automatically generated by asy-list.py.
;; Changes will be overwritten.
;;
(defvar asy-keywords-version "{args.revision}")
"""
)
)
# Define a function 'add' that adds a keyword to the output file.
def add(keyword):
keywords.write(keyword + " ")
# Write the beginning of the Emacs Lisp definition for 'asy-keyword-name'.
keywords.write("(defvar asy-keyword-name '(\n")
# Open the file 'camp.l' for reading.
with open("camp.l", "r", encoding="utf-8") as camp:
# Read lines from 'camp.l' until reaching a line that contains only '%%'.
for line in camp:
if re.search(r"^%%\s*$", line):
break
# Continue reading lines from 'camp.l' after the '%%' line.
for line in camp:
if re.search(r"^%%\s*$", line):
# Exit the loop when a second '%%' line is found, indicating the end of
# the section.
break
# Match lines that start with a word (the keyword) followed by optional
# whitespace and a '{'.
match = re.search(r"^(\w+)\s*\{", line)
if match:
# Write the keyword followed by a space.
keywords.write(match.group(1) + " ")
# Open an input file specified in the command-line arguments.
with open(args.asy_list_file, "r", encoding="utf-8") as asylist:
# Lists to store types, functions, and variables found in the file.
types = [] # List to hold data types.
functions = [] # List to hold function names.
variables = [] # List to hold variable names.
# Read each line from the file handle asylist.
for line in asylist:
# Match lines that define functions.
# The pattern looks for:
# - An optional word (\w*) representing the return type.
# - Any number of non-space characters ([^ ]*), which may include
# modifiers.
# - A space character.
# - Capture the function name (\w*).
# - An opening parenthesis '(', indicating the start of the parameter
# list.
matchFun = re.search(r"^(\w*)[^ ]* (\w*)\(", line)
if matchFun:
types.append(matchFun.group(1))
functions.append(matchFun.group(2))
# Match lines that declare variables.
# The pattern looks for:
# - Any non-space characters before a space ([^ ]*), representing the
# type.
# - A space character.
# - Capture the variable name (\w*).
# - A semicolon ';', indicating the end of the declaration.
matchVarDec = re.search(r"^([^ ]*) (\w*);", line)
if matchVarDec:
variables.append(matchVarDec.group(2))
# Remove duplicates and sort the lists.
types = sorted(set(types))
functions = sorted(set(functions))
variables = sorted(set(variables))
# Write the closing parentheses for the 'asy-keyword-name' definition in the
# output file.
keywords.write("))\n\n")
# Write the beginning of the 'asy-type-name' definition to the output file.
keywords.write("(defvar asy-type-name '(\n")
# Write each type from types to the output file.
for t in types:
keywords.write(t + " ") # Write the type followed by a space.
# Write the closing parentheses for the 'asy-type-name' definition.
keywords.write("))\n\n")
# Write the beginning of the 'asy-function-name' definition to the output
# file.
keywords.write("(defvar asy-function-name '(\n")
# Write each function name from functions to the output file.
for f in functions:
keywords.write(f + " ") # Write the function name followed by a space.
# Write the closing parentheses for the 'asy-function-name' definition.
keywords.write("))\n\n")
# Write the beginning of the 'asy-variable-name' definition to the output
# file.
keywords.write("(defvar asy-variable-name '(\n")
# Write each variable name from variables to the output file.
for v in variables:
keywords.write(v + " ") # Write the variable name followed by a space.
# Write the closing parentheses for the 'asy-variable-name' definition.
keywords.write("))\n")