Skip to content

Commit

Permalink
reader macro dispatch fix
Browse files Browse the repository at this point in the history
plus some comments
  • Loading branch information
markomanninen committed Apr 17, 2017
1 parent ffd0716 commit d9650f5
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 36 deletions.
86 changes: 52 additions & 34 deletions hyml/ext.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
#!/usr/bin/python3
# -*- coding: utf-8 -*-
#
#---------------------------------------------------------
# Copyright (c) Marko Manninen <[email protected]>, 2017
#---------------------------------------------------------
#
# Usage:
#
# entry_points = """
# [babel.extractors]
Expand All @@ -15,72 +18,88 @@
# extensions=hyml.ext.babel_extract
#
# to babel.cfg file
#
#-----------------------------------------------------------------------
# LOCALIZATION ROUTINE
#-----------------------------------------------------------------------
# 1. extract messages:
# pybabel extract -F babel.cfg -o messages.pot .
# 2. initialize language file:
# pybabel init -i messages.pot -d translations -l en
# 3. make changes to the language file (manually or via third party app)
# 4. update translation messages:
# pybabel update -i messages.pot -d translations
# 5. compile translations
# pybabel compile -d translations
#-----------------------------------------------------------------------

import hy, hy.importer as hyi
import itertools

# special reader macro symbol for translating string less verbose way.
# instead of (_ "message") in hy you can do i"message" as long as you have
# defined: (defreader i [args] `(_ ~@args)) in your program
readermacro = hy.HySymbol("i")
# defined: (defreader i [args] `(_ ~args)) in your program
readermacro, dispatch_reader_macro = hy.HySymbol("i"), hy.HySymbol("dispatch_reader_macro")

# accepted gettext / babel keywords
keywords = [hy.HySymbol("_"),
hy.HySymbol("gettext"),
hy.HySymbol("ngettext"),
hy.HySymbol("N_"),
hy.HySymbol("lgettext"),
hy.HySymbol("lngettext")]

# string and int are accepted as gettext messages
def is_message(e):
return not isinstance(e, hy.HySymbol) and (isinstance(e, hy.HyString) or isinstance(e, hy.HyInteger))
def is_message(expr):
return not isinstance(expr, hy.HySymbol) and (isinstance(expr, hy.HyString) or isinstance(expr, hy.HyInteger))

# create message dictionary
def message(e, f):
singular, plural, context = None, None, None
if f == 0:
singular = str(e)
elif f == 1:
plural = str(e)
def message(expr, count):
singular, plural, num = None, None, None
if count == 0:
singular = str(expr)
elif count == 1:
plural = str(expr)
else:
context = int(e)
return {"context":context, "singular":singular, "plural":plural}
num = int(expr)
return {"num":num, "singular":singular, "plural":plural}

def extract_from_ast(ast):
d, f = None, 0
def filter_hy(e):
current, count, prev, dispatch = None, 0, None, False
def filter_hy(expr):
# basicly we are searching for babel keyword expressions here
# and when one is found, it is returned along with:
# linenumber, keyword itself, and message string
global d, f
if isinstance(e, hy.HyExpression) or isinstance(e, list):
if isinstance(e, hy.HyExpression):
d, f = e[0], 0
global current, count, prev, dispatch
if isinstance(expr, hy.HyExpression) or isinstance(expr, list):
if isinstance(expr, hy.HyExpression):
current, count, prev, dispatch = expr[0], 0, None, False
# recursively filter expression so that only gettext and _ parts are returned
x, f = list(itertools.chain(*filter(None, map(filter_hy, e)))), 0
messages = list(itertools.chain(*filter(None, map(filter_hy, expr))))
# reset keyword
d = None
return x
if e == readermacro:
d, f = e, 0
elif is_message(e):
current, count, prev, dispatch = None, 0, None, False
return messages
if expr == readermacro and prev == dispatch_reader_macro:
current, count, dispatch = expr, 0, True
elif is_message(expr):
# reader macro is regarded as singular form gettext function
if d == readermacro:
if current == readermacro and dispatch:
# we dont accept any more argument for gettext / readermacro
d = None
return e.start_line, "gettext", message(e, 0)
current, prev, dispatch = None, None, False
return expr.start_line, "gettext", message(expr, 0)
# possible keys are:
# ngettext, pgettext, ungettext, dngettext, dgettext, ugettext, gettext, _, N_, npgettext
# but only gettext and _ are supported at the moment
if d in keywords:
if current in keywords:
# there are no comments available in ast, thus only three items are returned
# mark singular and plural forms. later in chunks
# plural and singular forms are combined. this is not particularly genious
# way of doing it. other recursive parsing technique could handle everything
# more efficiently
msg = message(e, f)
f += 1
return e.start_line, str(d), msg
msg = message(expr, count)
count += 1
return expr.start_line, str(current), msg
prev = expr
return filter_hy(ast)

# return list of message items
Expand All @@ -90,7 +109,7 @@ def items(l, i, n):
# detect plural and singular forms of messages
def message_form(t1, t2, t3):
if t2 and "plural" in t2[2] and t2[2]["plural"] != None:
return [t1[2]["singular"], t2[2]["plural"], t3[2]["context"]]
return [t1[2]["singular"], t2[2]["plural"], t3[2]["num"]]
else:
return [t1[2]["singular"]]

Expand All @@ -104,7 +123,6 @@ def chunks(l, n):

def babel_extract(fileobj, *args, **kw):
byte = fileobj.read()
# unfortunately line breaks (line numbers) are lost at this point...
text = "".join(map(chr, byte))
node = hyi.import_buffer_to_hst(text)
tpls = extract_from_ast(node)
Expand Down
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@
'hyml': ['*.hy']
},

version = 'v0.2.8',
version = 'v0.2.9',
description = 'HyML - XML / (X)HTML generator for Hy',
author = 'Marko Manninen',
author_email = '[email protected]',

url = 'https://github.com/markomanninen/hyml',
download_url = 'https://github.com/markomanninen/hyml/archive/v0.2.8.tar.gz',
download_url = 'https://github.com/markomanninen/hyml/archive/v0.2.9.tar.gz',
keywords = ['hylang', 'python', 'lisp', 'macros', 'markup language', 'dsl', 'xml', 'html', 'xhtml'],
platforms = ['any'],

Expand Down

0 comments on commit d9650f5

Please sign in to comment.