diff --git a/budou/budou.py b/budou/budou.py
index da1a28f..f547f61 100644
--- a/budou/budou.py
+++ b/budou/budou.py
@@ -26,6 +26,7 @@ from oauth2client.client import GoogleCredentials
 import oauth2client.service_account
 import re
 import six
+import unicodedata
 
 Chunk = collections.namedtuple('Chunk', ['word', 'pos', 'label', 'forward'])
 """Word chunk object.
@@ -158,12 +159,42 @@ class Budou(object):
       A list of Chunks.
     """
     chunks = self._get_source_chunks(input_text, language)
+    chunks = self._update_punct_direction(chunks)
     for forward in (True, False):
       condition = lambda chunk: (
           chunk.label in TARGET_LABEL or chunk.pos == 'PUNCT')
       chunks = self._concatenate_inner(chunks, condition, forward)
     return chunks
 
+  def _update_punct_direction(self, chunks):
+    """Updates chunk's concatenate direction if it is a punctuation mark.
+
+    Args:
+      chunks: A list of Chunks.
+
+    Returns:
+      A list of updated Chunks.
+    """
+    result = []
+    for chunk in chunks:
+      if chunk.pos == 'PUNCT':
+        forward = False
+        try:
+          # Getting unicode category to determine the direction.
+          # Concatenates to the following if it belongs to Ps or Pi category.
+          # Ps: Punctuation, open (e.g. opening bracket characters)
+          # Pi: Punctuation, initial quote (e.g. opening quotation mark)
+          # Otherwise, concatenates to the previous word.
+          # See also https://en.wikipedia.org/wiki/Unicode_character_property
+          category = unicodedata.category(chunk.word)
+          if category in ('Ps', 'Pi'):
+            forward = True
+        except:
+          pass
+        chunk = Chunk(chunk.word, chunk.pos, chunk.label, forward)
+      result.append(chunk)
+    return result
+
   def _get_attribute_dict(self, attributes, classname=None):
     """Returns a dictionary of attribute name-value pairs.
 

not proper grouping of comma  (読点)
<img width="354" alt="default" src="https://cloud.githubusercontent.com/assets/968152/24842452/bcee80fa-1dd2-11e7-848a-86b683f9dab6.png">

in this case, comma  (読点) should be grouped with former word (最終日).

<img width="357" alt="default" src="https://cloud.githubusercontent.com/assets/968152/24843094/48a93066-1dda-11e7-87f8-a684e52ebfc1.png">

<img width="389" alt="default" src="https://cloud.githubusercontent.com/assets/968152/24843125/a27b4160-1dda-11e7-9039-775f1542d099.png">

<img width="356" alt="default" src="https://cloud.githubusercontent.com/assets/968152/24843143/de3f7cf2-1dda-11e7-9ae7-320e84427d12.png">

<img width="364" alt="default" src="https://cloud.githubusercontent.com/assets/968152/24843153/f7d6d426-1dda-11e7-9c2a-82852ebf3d0d.png">

diff --git a/test/budou_test.py b/test/budou_test.py
index 8c3b30e..134487d 100644
--- a/test/budou_test.py
+++ b/test/budou_test.py
@@ -289,6 +289,36 @@ class TestBudouMethods(unittest.TestCase):
         result, expected,
         'Input text should be parsed into chunks separated by spaces.')
 
+  def test_update_punct_direction(self):
+    chunks = [
+        budou.Chunk(u'。', u'PUNCT', None, None),
+        budou.Chunk(u'、', u'PUNCT', None, None),
+        budou.Chunk(u'「', u'PUNCT', None, None),
+        budou.Chunk(u'」', u'PUNCT', None, None),
+        budou.Chunk(u'（', u'PUNCT', None, None),
+        budou.Chunk(u'）', u'PUNCT', None, None),
+        budou.Chunk(u'[', u'PUNCT', None, None),
+        budou.Chunk(u']', u'PUNCT', None, None),
+        budou.Chunk(u'(', u'PUNCT', None, None),
+        budou.Chunk(u')', u'PUNCT', None, None),
+    ]
+    expected = [
+        budou.Chunk(u'。', u'PUNCT', None, False),
+        budou.Chunk(u'、', u'PUNCT', None, False),
+        budou.Chunk(u'「', u'PUNCT', None, True),
+        budou.Chunk(u'」', u'PUNCT', None, False),
+        budou.Chunk(u'（', u'PUNCT', None, True),
+        budou.Chunk(u'）', u'PUNCT', None, False),
+        budou.Chunk(u'[', u'PUNCT', None, True),
+        budou.Chunk(u']', u'PUNCT', None, False),
+        budou.Chunk(u'(', u'PUNCT', None, True),
+        budou.Chunk(u')', u'PUNCT', None, False),
+    ]
+    result = self.parser._update_punct_direction(chunks)
+    self.assertEqual(
+        result, expected,
+        'Punctuation mark\'s concatenating direction should be determined.')
+
 
 if __name__ == '__main__':
   unittest.main()

pip install -e .
parse_log_pytest
google-api-python-client oauth2client lxml>=3.6.1 six
pytest mock
apt-get update apt-get install -y gcc
3.9
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
aiohappyeyeballs @ file:///croot/aiohappyeyeballs_1734469393482/work
aiohttp @ file:///croot/aiohttp_1734687138658/work
aiosignal @ file:///tmp/build/80754af9/aiosignal_1637843061372/work
async-timeout @ file:///croot/async-timeout_1732661977001/work
attrs @ file:///croot/attrs_1734533101012/work
Brotli @ file:///croot/brotli-split_1736182456865/work
-e git+https://github.com/google/budou.git@cb842ec0a8348245626af79ea755a51b658f7b73#egg=budou
cachetools @ file:///croot/cachetools_1738224668060/work
certifi @ file:///croot/certifi_1738623731865/work/certifi
cffi @ file:///croot/cffi_1736182485317/work
charset-normalizer @ file:///croot/charset-normalizer_1721748349566/work
cryptography @ file:///croot/cryptography_1740577825284/work
exceptiongroup==1.2.2
frozenlist @ file:///croot/frozenlist_1730902802621/work
google-api-core @ file:///croot/google-api-core-split_1742926780480/work
google-api-python-client @ file:///home/conda/feedstock_root/build_artifacts/google-api-python-client_1742419676831/work
google-auth @ file:///croot/google-auth_1741275169457/work
google-auth-httplib2 @ file:///home/conda/feedstock_root/build_artifacts/google-auth-httplib2_1733907410727/work
googleapis-common-protos @ file:///croot/googleapis-common-protos-feedstock_1742588908677/work
httplib2 @ file:///home/conda/feedstock_root/build_artifacts/httplib2_1733927481809/work
idna @ file:///croot/idna_1714398848350/work
iniconfig==2.1.0
lxml @ file:///croot/lxml_1737039601731/work
mock==5.2.0
multidict @ file:///croot/multidict_1730905498140/work
oauth2client @ file:///home/conda/feedstock_root/build_artifacts/oauth2client_1730205451224/work
packaging==24.2
pluggy==1.5.0
propcache @ file:///croot/propcache_1732303986938/work
proto-plus @ file:///croot/proto-plus_1742829342502/work
protobuf @ file:///croot/protobuf_1742419705443/work/bazel-bin/python/dist/protobuf-5.29.3-cp39-abi3-linux_x86_64.whl#sha256=99e7f032b8e72c85dbc84c9a250d19a4af2aeca12410c10349432035adb74ba0
pyasn1 @ file:///croot/pyasn1_1729239786406/work
pyasn1_modules @ file:///home/conda/feedstock_root/build_artifacts/pyasn1-modules_1733324602540/work
pycparser @ file:///tmp/build/80754af9/pycparser_1636541352034/work
pyOpenSSL @ file:///croot/pyopenssl_1741343803032/work
pyparsing @ file:///croot/pyparsing_1731445506121/work
PySocks @ file:///tmp/build/80754af9/pysocks_1605305812635/work
pytest==8.3.5
requests @ file:///croot/requests_1730999120400/work
rsa @ file:///tmp/build/80754af9/rsa_1614366226499/work
six @ file:///tmp/build/80754af9/six_1644875935023/work
tomli==2.2.1
typing_extensions @ file:///croot/typing_extensions_1734714854207/work
uritemplate @ file:///home/conda/feedstock_root/build_artifacts/uritemplate_1733927878495/work
urllib3 @ file:///croot/urllib3_1737133630106/work
yarl @ file:///croot/yarl_1732546845924/work

name: budou
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - aiohappyeyeballs=2.4.4=py39h06a4308_0
  - aiohttp=3.11.10=py39h5eee18b_0
  - aiosignal=1.2.0=pyhd3eb1b0_0
  - async-timeout=5.0.1=py39h06a4308_0
  - attrs=24.3.0=py39h06a4308_0
  - brotli-python=1.0.9=py39h6a678d5_9
  - ca-certificates=2025.2.25=h06a4308_0
  - cachetools=5.5.1=py39h06a4308_0
  - certifi=2025.1.31=py39h06a4308_0
  - cffi=1.17.1=py39h1fdaa30_1
  - charset-normalizer=3.3.2=pyhd3eb1b0_0
  - cryptography=44.0.1=py39h7825ff9_0
  - frozenlist=1.5.0=py39h5eee18b_0
  - google-api-core=2.24.2=py39h06a4308_0
  - google-api-python-client=2.165.0=pyhff2d567_0
  - google-auth=2.38.0=py39h06a4308_1
  - google-auth-httplib2=0.2.0=pyhd8ed1ab_1
  - googleapis-common-protos=1.69.2=py39h06a4308_0
  - httplib2=0.22.0=pyhd8ed1ab_1
  - icu=73.1=h6a678d5_0
  - idna=3.7=py39h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libabseil=20250127.0=cxx17_h6a678d5_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - libxml2=2.13.5=hfdd30dd_0
  - libxslt=1.1.41=h097e994_0
  - lxml=5.3.0=py39h57af460_1
  - multidict=6.1.0=py39h5eee18b_0
  - ncurses=6.4=h6a678d5_0
  - oauth2client=4.1.3=pyhd8ed1ab_1
  - openssl=3.0.16=h5eee18b_0
  - pip=25.0=py39h06a4308_0
  - propcache=0.2.0=py39h5eee18b_0
  - proto-plus=1.26.1=py39h06a4308_0
  - protobuf=5.29.3=py39he621ea3_0
  - pyasn1=0.6.1=py39h06a4308_0
  - pyasn1-modules=0.4.1=pyhd8ed1ab_1
  - pycparser=2.21=pyhd3eb1b0_0
  - pyopenssl=25.0.0=py39h06a4308_0
  - pyparsing=3.2.0=py39h06a4308_0
  - pysocks=1.7.1=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - requests=2.32.3=py39h06a4308_1
  - rsa=4.7.2=pyhd3eb1b0_1
  - setuptools=75.8.0=py39h06a4308_0
  - six=1.16.0=pyhd3eb1b0_1
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - typing-extensions=4.12.2=py39h06a4308_0
  - typing_extensions=4.12.2=py39h06a4308_0
  - tzdata=2025a=h04d1e81_0
  - uritemplate=4.1.1=pyhd8ed1ab_1
  - urllib3=2.3.0=py39h06a4308_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - yarl=1.18.0=py39h5eee18b_0
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - exceptiongroup==1.2.2
      - iniconfig==2.1.0
      - mock==5.2.0
      - packaging==24.2
      - pluggy==1.5.0
      - pytest==8.3.5
      - tomli==2.2.1
prefix: /opt/conda/envs/budou

test/budou_test.py::TestBudouMethods::test_update_punct_direction
test/budou_test.py::TestBudouMethods::test_cache test/budou_test.py::TestBudouMethods::test_get_attribute_dict test/budou_test.py::TestBudouMethods::test_get_chunks_per_space test/budou_test.py::TestBudouMethods::test_get_elements_list test/budou_test.py::TestBudouMethods::test_get_source_chunks test/budou_test.py::TestBudouMethods::test_migrate_html test/budou_test.py::TestBudouMethods::test_parse_ja test/budou_test.py::TestBudouMethods::test_parse_ko test/budou_test.py::TestBudouMethods::test_preprocess test/budou_test.py::TestBudouMethods::test_process_with_aria test/budou_test.py::TestBudouMethods::test_spanize
budou/budou.py
budou/budou.py