völlige neuüberarbeitung des yaml-Referenzparsers

git-svn-id: https://svn.neo-layout.org@1560 b9310e46-f624-0410-8ea1-cfbb3a30dc96
This commit is contained in:
martin_r 2009-01-29 20:57:24 +00:00
parent 881ad586c4
commit f59db1b5cf
5 changed files with 230 additions and 189 deletions

View file

@ -1,185 +0,0 @@
#YAML Parser for the Neo reference
#Copyright 2009 Martin Roppelt (m.p.roppelt ἢτ web in Germany)
#
#This file is part of German NEO-Layout Version 2.
#German Neo Layout Version 2 is free software: you can redistribute it and/or
#modify it under the terms of the GNU General Public License as published by the
#Free Software Foundation, either version 3 of the License, or (at your option)
#any later version. You should have received a copy of the GNU General Public
#License along with German NEO-Layout Version 2. If not, see
#<http://www.gnu.org/licenses/>.
'''
Converts the reference into both human and machine readable and editable files
for automated creating of keyboard drivers, pictures and references.
'''
#Needs at least Phyton 3.0 and PyYAML 3.08 (pyyaml.org) to run.
#
#Call with -h|--help to print command line options.
#
#Variables:
#b: box drawing characters as tuple,
#index bits: (0) left (1) down (2) right (3) up
##c: control file #for reference
#f: file iterator
#i: index-sorted file
#m: model file
#min: miniatures
#n: name/number iterator
#o: command line options:
#o.a: auto-completion of file names as boolean
#o.d: destination file name
#o.i: index-sorted file name
#o.m: model file name
#o.r: paths relative to ./ otherwise to ../A-REFERENZ-A/ as boolean
#o.s: source file name
#o.t: destination file types (i/m/p/v)
#o.v: view file name
#p: pattern file
#pm: pre-model
#r: compiled regular expression
#s: source file as string
#v: view file, contains:
#pv: pre-view
# string literals
# replacement commands
# patterns
from neo20_global import b
from optparse import OptionParser, make_option
from sys import stdout
from unicodedata import category
import os
import re
import yaml
def parse(s, w = 5):
'''
Returns a representation of a key field s with the standard with w.
'''
#Variables:
#i: line iterator
#j: level iterator
#l: key line iterator
#y: key level list
#m: model
#k: key iterator
#s: key field as string
#w: standard key with
#v: view
#q: key iterator
m = []; v = []; y = []; i = 0
for l in [l[:s.index(b[3])] for l in s.splitlines()[1:]]:
if l[0] in (b[12], b[14]):
m.append([]); v.append([])
for j, l in enumerate(y[::-1]):
q = 0
for k in l[1:].split(b[10]):
if j == 0:
m[i].append([]); v[i].append([])
if len(k) == w:
if w == 7:
m[i][q].extend([k[0], k[2:5], k[6:7]])
elif w == 5:
m[i][q].extend(list(k[0:5:2]))
elif w == 1:
m[i][q] = k
else:
v[i][q] = k
q += 1
i += 1; y = []
else:
y.append(l)
return m, v
def compare(m, min):
'''
Compares the overall and miniature key field and returns their completed
views.
'''
v = [m[1] for m in m]
return v
#command line options:
o = OptionParser(usage = 'example: %prog -ti -astest',
description = 'YAML Parser for the Neo reference',
option_list = [
make_option('-a', action = 'store_true', default = False, help = 'source file \
name is inserted into neo20- and .txt'),
make_option('-r', action = 'store_false', default = True, help = 'source and \
destination files are not relative to ../A-REFERENZ-A/'),
make_option('-t', metavar = 'mxiv', default = 'm', help = 'destination file \
types (default = %default)'),
make_option('-s', metavar = 'source file', default = 'neo20.txt',
help = 'default = %default'),
make_option('-d', metavar = 'destination file name', help = 'default = *.*'),
make_option('-m', metavar = 'model file', help = 'default = *.model'),
make_option('-x', metavar = 'hex model file', help = 'default = *.x'),
make_option('-i', metavar = 'index-sorted file', help = 'default = *.index'),
make_option('-v', metavar = 'view file', help = 'default = *.view')]
).parse_args()[0]
#evaluate options:
o.t = o.t.lower()
if o.d == None:
o.d = o.s.rsplit('.txt')[0]
if o.a:
o.s, o.d = 'neo20-' + o.s + '.txt', 'neo20-' + o.d
if o.r:
o.s, o.d = '../A-REFERENZ-A/' + o.s, '../A-REFERENZ-A/' + o.d
for f in '.model', '.x', '.index', '.view':
if f[1] in o.t.lower() and eval('o.' + f[1]) == None:
exec('o.' + f[1] + ' = o.d + f')
#input:
s = open(o.s, encoding = 'utf8').read()
#processing:
r = re.compile(b[6] + '.*?' + b[9] + '.*?\n(?=\n)', re.DOTALL | re.MULTILINE)
p, pm = r.split(s), r.findall(s) #Split into key fields and the rest
for n in (0, 16):
p.insert(n, p.pop(n) + pm.pop(n) + p.pop(n)) #Put the legends back into the
#pattern list
#parse key fields
m = [parse(pm[9]), parse(pm[20], 7)]
min = []
for n in range(6):
min.append([parse(pm[10 + n], 1), parse(pm[21+ n], 7)])
v = compare(m, min); m = [m[0] for m in m]
#complete view ##
v = [[v], []]
v.append(p)
#create hex model
if 'x' in o.t.lower():
x = []
for f, n in enumerate(m):
x.append(); ##
#create index
if 'i' in o.t.lower():
i = []
for n in m:
for n in n:
i.extend(n)
#output:
for f in 'mxiv':
if not stdout.isatty():
file = stdout
else:
file = open(eval('o.' + f), 'w', encoding = 'utf8')
if f in o.t:
if f == 'v':
yaml.dump_all(v[0:-1], file, allow_unicode = True,
explicit_start = True,
explicit_end = True)
yaml.dump(v.pop(), file, allow_unicode = True,
explicit_start = True,
explicit_end = True,
default_style = '|')
else:
yaml.dump(eval(f), file, allow_unicode = True,
explicit_start = True,
explicit_end = True)

View file

@ -1,3 +0,0 @@
#define box drawing
b = None, None, None, '\u2510', None, '\u2500', '\u250C', '\u252C', None, \
'\u2518', '\u2502', '\u2524', '\u2514', '\u2534', '\u251C', '\u253C'

27
yaml/neo_import.py Normal file
View file

@ -0,0 +1,27 @@
#===============================================================================
# Imports:
#===============================================================================
#===============================================================================
# define box drawings:
# index bits: (0) left (1) down (2) right (3) up
#===============================================================================
box_drawings = None, None, None, '\u2510', \
None, '\u2500', '\u250C', '\u252C', \
None, '\u2518', '\u2502', '\u2524', \
'\u2514', '\u2534', '\u251C', '\u253C'
#===============================================================================
# Defaults:
#===============================================================================
reference_directory = '../A-REFERENZ-A/'
file_name_append_start = 'neo20-'
file_name_append_end = '.txt'
file_name_standard_extension = file_name_append_end
# synchronize with program!
file_extensions_mapping = {('model_file', 'm'):('.model','model'),
('hex_model_file', 'x'):('.hex', 'hex_model'),
('index_sorted_file', 'i'):('.index', 'index'),
('view_file', 'v'):('.view', 'view')}

180
yaml/parse_neo.py Normal file
View file

@ -0,0 +1,180 @@
#===============================================================================
# YAML Parser for the Neo reference
# Copyright 2009 Martin Roppelt (m.p.roppelt ἢτ web in Germany)
#
# This file is part of German NEO-Layout Version 2.
# German Neo Layout Version 2 is free software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at your
# option) any later version. You should have received a copy of the GNU General
# Public License along with German NEO-Layout Version 2. If not, see
# <http://www.gnu.org/licenses/>.
#===============================================================================
'''
Converts the reference into both human and machine readable and editable files
for automated creating of keyboard drivers, pictures and references.
'''
#===============================================================================
# Needs at least Phyton 3.0 and PyYAML 3.08 (pyyaml.org) to run.
#
# Call with -h|--help to print command line options.
#===============================================================================
#===============================================================================
# To Do:
#===============================================================================
from neo_import import * #neo shared, settings and function module
from optparse import make_option, OptionParser #command line analyzing module
import re #regular expression processing module: compile
import sys #system module: stdout
import yaml #YAML processing module: dump
#===============================================================================
# analyze command line options:
#===============================================================================
options = OptionParser(usage = 'example: %prog -ti -astest', description = 'YAML Parser for the Neo reference',
option_list = [
make_option('-a', '--append', '--neo20-txt', action = 'store_true', default = False, help = 'source file name is inserted into ' + file_name_append_start + ' and ' + file_name_append_end + ''),
make_option('-r', '--path-not-relative', dest = 'path_relative', action = 'store_false', default = True, help = 'source and destination files are not relative to ' + reference_directory),
make_option('-t', '--types', metavar = 'mxiv', default = 'm', help = 'destination file types (default = %default)'),
make_option('-s', '--source-file', metavar = 'source file', default = 'neo20.txt', help = 'default = %default'),
make_option('-d', '--destination-file', metavar = 'destination file', help = 'default = *.*'),
make_option('-m', '--model-file', metavar = 'model file', help = 'default = *.model'),
make_option('-x', '--hex-model-file', metavar = 'hex model file', help = 'default = *.x'),
make_option('-i', '--index-sorted-file', metavar = 'index-sorted file', help = 'default = *.index'),
make_option('-v', '--view-file', metavar = 'view file', help = 'default = *.view'),
make_option('-p', '--plain-panel', '--without-legend', dest = 'without_legend', action = 'store_true', default = False, help = 'plain panel reference (without legends)'),
make_option('-w', '--key-level-width', type = 'int', metavar = 'int', default = 1, help = 'default = %default'),
make_option('-W', '--key-level-delimiter-width', type = 'int', metavar = 'int', default = 1, help = 'default = %default'),
make_option('-f', '--key-level-delimiter-filler', metavar = 'char',default = " ", help = 'default = %default'),
make_option('-D', '--key-level-delimiter', metavar = 'char'),
make_option('-l', '--key-levels-per-line', type = 'int', metavar = 'int', default = 3, help = 'default = %default')
]).parse_args()[0]
if options.destination_file == None:
options.destination_file = options.source_file.rsplit(file_name_standard_extension)[0]
if options.append:
options.source_file = file_name_append_start + options.source_file + file_name_append_end
options.destination_file = file_name_append_start + options.destination_file
if options.path_relative:
options.source_file = reference_directory + options.source_file
options.destination_file = reference_directory + options.destination_file
for option, type in file_extensions_mapping:
if type in options.types and eval('options.' + option) == None:
exec('options.' + option + '=\'' + options.destination_file + file_extensions_mapping[option, type][0] + '\'')
if options.key_level_delimiter == None:
options.key_level_delimiter = "".ljust(options.key_level_delimiter_width, options.key_level_delimiter_filler)
#===============================================================================
# functions:
#===============================================================================
# assumption: no delimiters around key level block
def parse_key_panel(key_panel, key_width = 5):
'''
returns the model and view representing key_panel,
which contain lists for each key row,
which contain lists for each key in the key row,
which are lists for the key level strings.
'''
return_model = []; return_view = []; key_row = []; key_row_index = 0 # initialization
for row in [row[:key_panel.index(box_drawings[3])] for row in key_panel.splitlines()[1:]]: # omit beginning and ending box drawings
if row[0] in (box_drawings[12], box_drawings[14]): # if row begins with ├ or │,
return_model.append([]); return_view.append([]) # create key row list
key_lines = []
for key_line_index, key_line in enumerate(key_row[:: - 1]): # reverse lines
for key_index, key in enumerate(key_line[1:].split(box_drawings[10])): # split key line into keys
if key_line_index == 0: # if bottom line,
return_model[key_row_index].append([]); key_lines.append([]); return_view[key_row_index].append([]) # create key list
if len(key) == key_width: # if default key, parse line levels:
if options.key_level_width == 1 and key_width == 7: # if Neo 2 keypad key:
if key[1] == key[5] == options.key_level_delimiter:
key_lines[key_index].extend([key[0], key[2:5], key[6]])
elif key[::2] == options.key_level_delimiter * 4:
key_lines[key_index].extend(key[1::2])
else:
return_view[key_row_index][key_index].append(key)
elif key_width == options.key_level_width * options.key_levels_per_line + (options.key_levels_per_line - 1) * options.key_level_delimiter_width:
key_lines[key_index].extend([key[:options.key_level_width] for key in key[:key_width:options.key_level_width + options.key_level_delimiter_width]])
elif key_width == options.key_level_width:
key_lines[key_index].append(key)
else:
if options.key_level_width == 1 and key_width == 7 and len(key) == 15: # if Neo 2 keypad key:
key_lines[key_index].extend([key[2], key[6:9], key[12]])
else:
return_view[key_row_index][key_index].append(key)
if key_lines != []:
for key_level_index, level in enumerate(key_lines[0]):
for key_line_index, line_index in enumerate(key_lines):
return_model[key_row_index][key_index].append(key_lines[key_line_index][key_level_index])
key_row_index += 1; key_row = []
else:
key_row.append(row)
return return_model, return_view
def compare_model(model, miniature_model):
'''
Compares the overall and miniature key field and returns their completed
views.
'''
return [model[1] for model in model]
#===============================================================================
# read in reference:
#===============================================================================
source = open(options.source_file, encoding = 'utf8').read()
#===============================================================================
# split reference into lists of key panels and illustrative text patterns
#===============================================================================
# searches for ┌…┘ and the following line:
regex = re.compile(box_drawings[6] + '.*?' + box_drawings[9] + '.*?\n(?=\n)', re.DOTALL | re.MULTILINE)
legends, key_panels = regex.split(source), regex.findall(source) # split into lists of key panels and illustrative texts
if options.without_legend == False:
for legend_index in (0, 16):
legends.insert(legend_index, legends.pop(legend_index)+ key_panels.pop(legend_index) + legends.pop(legend_index)) # put the legends back into the pattern list
#parse key fields
model = [parse_key_panel(key_panels[9]), parse_key_panel(key_panels[20], 7)]
miniature_models = []
for miniature_models_index in range(6):
miniature_models.append([parse_key_panel(key_panels[10 + miniature_models_index], 1), parse_key_panel(key_panels[21 + miniature_models_index], 7)])
view = compare_model(model, miniature_models) # complete views via comparing key widths
model = [model[0] for model in model] # strip views
# complete view ##
view = [[view], [], legends]
# create hex model
if 'x' in options.types:
hex_model = []
for row_index, row in enumerate(model):
hex_model.append([])
for key_index, key in enumerate(row):
hex_model[row_index].append([])
for level in key:
if len(level) == 1:
hex_index[row_index][key_index].append(hex(ord(level))[2:].rjust(4,' '))
# create index
if 'i' in options.types:
index = []
for n in model:
for n in n:
i.extend(n)
# output:
for option, type in file_extensions_mapping:
if type in options.types:
if not sys.stdout.isatty():
file = sys.stdout
else:
file = open(eval('options.' + option), 'w', encoding = 'utf8')
if type == 'v':
yaml.dump_all(view[0: - 1], file, allow_unicode = True)
yaml.dump(view.pop(), file, explicit_start = True, allow_unicode = True, default_style = '|')
else:
yaml.dump(eval(file_extensions_mapping[option, type][1]), file, allow_unicode = True)

View file

@ -36,4 +36,26 @@ rasch aus Belegungen Neo-3-Treiber und Forks erstellen.
Projektstatus:
Zur Zeit entwickele ich einen Parser für die Referenz. Danach möchte ich ein
Skript für die Erstellung der neo20.txt aus der maschinenlesbaren Referenz
schreiben.
schreiben. Dann soll ein Skript zur Umwandlung des Models in xkbmap, xmodmap,
ahk und kbdneo folgen, unter berücksichtigung der verwendeten Tastatur (Qwertz,
Qwerty, Plum, Kbdneo).
Abriss:
neo_import.py
neo_parse.py
neo_edit.py
neo_make.py
ahk_make.py
ahk_parse.py
hex_parse.py
kbd_parse.py
kbd_make.py
xkb_parse.py
xkb_parse.py
mod_parse.py
mod_make.py
mac_parse.py
mac_make.py
map_parse.py
svg_parse.py
svg_make.py