Codebase list python-magic-ahupp / 4e4050f
New upstream version 0.4.13 Sophie Brun 6 years ago
20 changed file(s) with 814 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0 deb_dist
1 python_magic.egg-info
0 language: python
1
2 # needed to use trusty
3 sudo: required
4
5 dist: trusty
6
7 python:
8 - "2.6"
9 - "2.7"
10 - "3.3"
11 - "3.4"
12 - "3.5"
13 - "3.6"
14 - "nightly"
15
16 install:
17 - pip install coveralls
18 - pip install codecov
19 - python setup.py install
20
21 script:
22 - coverage run setup.py test
23
24 after_success:
25 - coveralls
26 - codecov
0 The MIT License (MIT)
1
2 Copyright (c) 2001-2014 Adam Hupp
3
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
10
11 The above copyright notice and this permission notice shall be included in all
12 copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 SOFTWARE.
0 include *.py
1 include LICENSE
2 include test/testdata/*
3 include test/*.sh
0 # python-magic
1 [![PyPI version](https://badge.fury.io/py/python-magic.svg)](https://badge.fury.io/py/python-magic)
2 [![Build Status](https://travis-ci.org/ahupp/python-magic.svg?branch=master)](https://travis-ci.org/ahupp/python-magic)
3
4 python-magic is a python interface to the libmagic file type
5 identification library. libmagic identifies file types by checking
6 their headers according to a predefined list of file types. This
7 functionality is exposed to the command line by the Unix command
8 `file`.
9
10 ## Usage
11
12 ```python
13 >>> import magic
14 >>> magic.from_file("testdata/test.pdf")
15 'PDF document, version 1.2'
16 >>> magic.from_buffer(open("testdata/test.pdf").read(1024))
17 'PDF document, version 1.2'
18 >>> magic.from_file("testdata/test.pdf", mime=True)
19 'application/pdf'
20 ```
21
22 There is also a `Magic` class that provides more direct control,
23 including overriding the magic database file and turning on character
24 encoding detection. This is not recommended for general use. In
25 particular, it's not safe for sharing across multiple threads and
26 will fail throw if this is attempted.
27
28 ```python
29 >>> f = magic.Magic(uncompress=True)
30 >>> f.from_file('testdata/test.gz')
31 'ASCII text (gzip compressed data, was "test", last modified: Sat Jun 28
32 21:32:52 2008, from Unix)'
33 ```
34
35 You can also combine the flag options:
36
37 ```python
38 >>> f = magic.Magic(mime=True, uncompress=True)
39 >>> f.from_file('testdata/test.gz')
40 'text/plain'
41 ```
42
43 ## Name Conflict
44
45 There are, sadly, two libraries which use the module name `magic`. Both have been around for quite a while.If you are using this module and get an error using a method like `open`, your code is expecting the other one. Hopefully one day these will be reconciled.
46
47 ## Installation
48
49 The current stable version of python-magic is available on pypi and
50 can be installed by running `pip install python-magic`.
51
52 Other sources:
53
54 - pypi: http://pypi.python.org/pypi/python-magic/
55 - github: https://github.com/ahupp/python-magic
56
57 ### Dependencies
58
59 On Windows, copy magic1.dll, regex2.dll, and zlib1.dll onto your PATH from the Binaries and Dependencies zipfiles provided by the [File for Windows](http://gnuwin32.sourceforge.net/packages/file.htm) project. You will need to copy the file `magic` out of `[binary-zip]\share\misc`, and pass it's location to `Magic(magic_file=...)`. If you are using a 64-bit build of python, you'll need 64-bit libmagic binaries which can be found here: https://github.com/pidydx/libmagicwin64 (note: untested)
60
61 On OSX:
62
63 - When using Homebrew: `brew install libmagic`
64 - When using macports: `port install file`
65
66 ### Troubleshooting
67
68 - 'MagicException: could not find any magic files!': some
69 installations of libmagic do not correctly point to their magic
70 database file. Try specifying the path to the file explicitly in the
71 constructor: `magic.Magic(magic_file="path_to_magic_file")`.
72
73 - 'WindowsError: [Error 193] %1 is not a valid Win32 application':
74 Attempting to run the 32-bit libmagic DLL in a 64-bit build of
75 python will fail with this error. Here are 64-bit builds of libmagic for windows: https://github.com/pidydx/libmagicwin64
76
77 - 'WindowsError: exception: access violation writing 0x00000000 ' This may indicate you are mixing
78 Windows Python and Cygwin Python. Make sure your libmagic and python builds are consistent.
79
80 ## Author
81
82 Written by Adam Hupp in 2001 for a project that never got off the
83 ground. It originally used SWIG for the C library bindings, but
84 switched to ctypes once that was part of the python standard library.
85
86 You can contact me via my [website](http://hupp.org/adam) or
87 [github](http://github.com/ahupp).
88
89 ## Contributors
90
91 Thanks to these folks on github who submitted features and bugfixes.
92
93 - Amit Sethi
94 - [bigben87](https://github.com/bigben87)
95 - [fallgesetz](https://github.com/fallgesetz)
96 - [FlaPer87](https://github.com/FlaPer87)
97 - [lukenowak](https://github.com/lukenowak)
98 - NicolasDelaby
99 - [email protected]
100 - SimpleSeb
101 - [tehmaze](https://github.com/tehmaze)
102
103 ## License
104
105 python-magic is distributed under the MIT license. See the included
106 LICENSE file for details.
107
108
(New empty file)
0 """
1 magic is a wrapper around the libmagic file identification library.
2
3 See README for more information.
4
5 Usage:
6
7 >>> import magic
8 >>> magic.from_file("testdata/test.pdf")
9 'PDF document, version 1.2'
10 >>> magic.from_file("testdata/test.pdf", mime=True)
11 'application/pdf'
12 >>> magic.from_buffer(open("testdata/test.pdf").read(1024))
13 'PDF document, version 1.2'
14 >>>
15
16
17 """
18
19 import sys
20 import glob
21 import os.path
22 import ctypes
23 import ctypes.util
24 import threading
25
26 from ctypes import c_char_p, c_int, c_size_t, c_void_p
27
28
29 class MagicException(Exception):
30 def __init__(self, message):
31 super(MagicException, self).__init__(message)
32 self.message = message
33
34
35 class Magic:
36 """
37 Magic is a wrapper around the libmagic C library.
38
39 """
40
41 def __init__(self, mime=False, magic_file=None, mime_encoding=False,
42 keep_going=False, uncompress=False):
43 """
44 Create a new libmagic wrapper.
45
46 mime - if True, mimetypes are returned instead of textual descriptions
47 mime_encoding - if True, codec is returned
48 magic_file - use a mime database other than the system default
49 keep_going - don't stop at the first match, keep going
50 uncompress - Try to look inside compressed files.
51 """
52 self.flags = MAGIC_NONE
53 if mime:
54 self.flags |= MAGIC_MIME
55 if mime_encoding:
56 self.flags |= MAGIC_MIME_ENCODING
57 if keep_going:
58 self.flags |= MAGIC_CONTINUE
59
60 if uncompress:
61 self.flags |= MAGIC_COMPRESS
62
63 self.cookie = magic_open(self.flags)
64 self.lock = threading.Lock()
65
66 magic_load(self.cookie, magic_file)
67
68 def from_buffer(self, buf):
69 """
70 Identify the contents of `buf`
71 """
72 with self.lock:
73 try:
74 return maybe_decode(magic_buffer(self.cookie, buf))
75 except MagicException as e:
76 return self._handle509Bug(e)
77
78 def from_file(self, filename):
79 # raise FileNotFoundException or IOError if the file does not exist
80 with open(filename):
81 pass
82 with self.lock:
83 try:
84 return maybe_decode(magic_file(self.cookie, filename))
85 except MagicException as e:
86 return self._handle509Bug(e)
87
88 def _handle509Bug(self, e):
89 # libmagic 5.09 has a bug where it might fail to identify the
90 # mimetype of a file and returns null from magic_file (and
91 # likely _buffer), but also does not return an error message.
92 if e.message is None and (self.flags & MAGIC_MIME):
93 return "application/octet-stream"
94 else:
95 raise e
96
97 def __del__(self):
98 # no _thread_check here because there can be no other
99 # references to this object at this point.
100
101 # during shutdown magic_close may have been cleared already so
102 # make sure it exists before using it.
103
104 # the self.cookie check should be unnecessary and was an
105 # incorrect fix for a threading problem, however I'm leaving
106 # it in because it's harmless and I'm slightly afraid to
107 # remove it.
108 if self.cookie and magic_close:
109 magic_close(self.cookie)
110 self.cookie = None
111
112 _instances = {}
113
114 def _get_magic_type(mime):
115 i = _instances.get(mime)
116 if i is None:
117 i = _instances[mime] = Magic(mime=mime)
118 return i
119
120 def from_file(filename, mime=False):
121 """"
122 Accepts a filename and returns the detected filetype. Return
123 value is the mimetype if mime=True, otherwise a human readable
124 name.
125
126 >>> magic.from_file("testdata/test.pdf", mime=True)
127 'application/pdf'
128 """
129 m = _get_magic_type(mime)
130 return m.from_file(filename)
131
132 def from_buffer(buffer, mime=False):
133 """
134 Accepts a binary string and returns the detected filetype. Return
135 value is the mimetype if mime=True, otherwise a human readable
136 name.
137
138 >>> magic.from_buffer(open("testdata/test.pdf").read(1024))
139 'PDF document, version 1.2'
140 """
141 m = _get_magic_type(mime)
142 return m.from_buffer(buffer)
143
144
145
146
147 libmagic = None
148 # Let's try to find magic or magic1
149 dll = ctypes.util.find_library('magic') or ctypes.util.find_library('magic1') or ctypes.util.find_library('cygmagic-1')
150
151 # This is necessary because find_library returns None if it doesn't find the library
152 if dll:
153 libmagic = ctypes.CDLL(dll)
154
155 if not libmagic or not libmagic._name:
156 windows_dlls = ['magic1.dll','cygmagic-1.dll']
157 platform_to_lib = {'darwin': ['/opt/local/lib/libmagic.dylib',
158 '/usr/local/lib/libmagic.dylib'] +
159 # Assumes there will only be one version installed
160 glob.glob('/usr/local/Cellar/libmagic/*/lib/libmagic.dylib'),
161 'win32': windows_dlls,
162 'cygwin': windows_dlls,
163 'linux': ['libmagic.so.1'], # fallback for some Linuxes (e.g. Alpine) where library search does not work
164 }
165 platform = 'linux' if sys.platform.startswith('linux') else sys.platform
166 for dll in platform_to_lib.get(platform, []):
167 try:
168 libmagic = ctypes.CDLL(dll)
169 break
170 except OSError:
171 pass
172
173 if not libmagic or not libmagic._name:
174 # It is better to raise an ImportError since we are importing magic module
175 raise ImportError('failed to find libmagic. Check your installation')
176
177 magic_t = ctypes.c_void_p
178
179 def errorcheck_null(result, func, args):
180 if result is None:
181 err = magic_error(args[0])
182 raise MagicException(err)
183 else:
184 return result
185
186 def errorcheck_negative_one(result, func, args):
187 if result is -1:
188 err = magic_error(args[0])
189 raise MagicException(err)
190 else:
191 return result
192
193
194 # return str on python3. Don't want to unconditionally
195 # decode because that results in unicode on python2
196 def maybe_decode(s):
197 if str == bytes:
198 return s
199 else:
200 return s.decode('utf-8')
201
202 def coerce_filename(filename):
203 if filename is None:
204 return None
205
206 # ctypes will implicitly convert unicode strings to bytes with
207 # .encode('ascii'). If you use the filesystem encoding
208 # then you'll get inconsistent behavior (crashes) depending on the user's
209 # LANG environment variable
210 is_unicode = (sys.version_info[0] <= 2 and
211 isinstance(filename, unicode)) or \
212 (sys.version_info[0] >= 3 and
213 isinstance(filename, str))
214 if is_unicode:
215 return filename.encode('utf-8')
216 else:
217 return filename
218
219 magic_open = libmagic.magic_open
220 magic_open.restype = magic_t
221 magic_open.argtypes = [c_int]
222
223 magic_close = libmagic.magic_close
224 magic_close.restype = None
225 magic_close.argtypes = [magic_t]
226
227 magic_error = libmagic.magic_error
228 magic_error.restype = c_char_p
229 magic_error.argtypes = [magic_t]
230
231 magic_errno = libmagic.magic_errno
232 magic_errno.restype = c_int
233 magic_errno.argtypes = [magic_t]
234
235 _magic_file = libmagic.magic_file
236 _magic_file.restype = c_char_p
237 _magic_file.argtypes = [magic_t, c_char_p]
238 _magic_file.errcheck = errorcheck_null
239
240 def magic_file(cookie, filename):
241 return _magic_file(cookie, coerce_filename(filename))
242
243 _magic_buffer = libmagic.magic_buffer
244 _magic_buffer.restype = c_char_p
245 _magic_buffer.argtypes = [magic_t, c_void_p, c_size_t]
246 _magic_buffer.errcheck = errorcheck_null
247
248 def magic_buffer(cookie, buf):
249 return _magic_buffer(cookie, buf, len(buf))
250
251
252 _magic_load = libmagic.magic_load
253 _magic_load.restype = c_int
254 _magic_load.argtypes = [magic_t, c_char_p]
255 _magic_load.errcheck = errorcheck_negative_one
256
257 def magic_load(cookie, filename):
258 return _magic_load(cookie, coerce_filename(filename))
259
260 magic_setflags = libmagic.magic_setflags
261 magic_setflags.restype = c_int
262 magic_setflags.argtypes = [magic_t, c_int]
263
264 magic_check = libmagic.magic_check
265 magic_check.restype = c_int
266 magic_check.argtypes = [magic_t, c_char_p]
267
268 magic_compile = libmagic.magic_compile
269 magic_compile.restype = c_int
270 magic_compile.argtypes = [magic_t, c_char_p]
271
272
273
274 MAGIC_NONE = 0x000000 # No flags
275 MAGIC_DEBUG = 0x000001 # Turn on debugging
276 MAGIC_SYMLINK = 0x000002 # Follow symlinks
277 MAGIC_COMPRESS = 0x000004 # Check inside compressed files
278 MAGIC_DEVICES = 0x000008 # Look at the contents of devices
279 MAGIC_MIME = 0x000010 # Return a mime string
280 MAGIC_MIME_ENCODING = 0x000400 # Return the MIME encoding
281 MAGIC_CONTINUE = 0x000020 # Return all matches
282 MAGIC_CHECK = 0x000040 # Print warnings to stderr
283 MAGIC_PRESERVE_ATIME = 0x000080 # Restore access time on exit
284 MAGIC_RAW = 0x000100 # Don't translate unprintable chars
285 MAGIC_ERROR = 0x000200 # Handle ENOENT etc as real errors
286
287 MAGIC_NO_CHECK_COMPRESS = 0x001000 # Don't check for compressed files
288 MAGIC_NO_CHECK_TAR = 0x002000 # Don't check for tar files
289 MAGIC_NO_CHECK_SOFT = 0x004000 # Don't check magic entries
290 MAGIC_NO_CHECK_APPTYPE = 0x008000 # Don't check application type
291 MAGIC_NO_CHECK_ELF = 0x010000 # Don't check for elf details
292 MAGIC_NO_CHECK_ASCII = 0x020000 # Don't check for ascii files
293 MAGIC_NO_CHECK_TROFF = 0x040000 # Don't check ascii/troff
294 MAGIC_NO_CHECK_FORTRAN = 0x080000 # Don't check ascii/fortran
295 MAGIC_NO_CHECK_TOKENS = 0x100000 # Don't check ascii/tokens
0 [global]
1 command_packages=stdeb.command
2
3 [bdist_wheel]
4 universal = 1
0 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2
3 from setuptools import setup
4
5 setup(name='python-magic',
6 description='File type identification using libmagic',
7 author='Adam Hupp',
8 author_email='[email protected]',
9 url="http://github.com/ahupp/python-magic",
10 version='0.4.13',
11 py_modules=['magic'],
12 long_description="""This module uses ctypes to access the libmagic file type
13 identification library. It makes use of the local magic database and
14 supports both textual and MIME-type output.
15 """,
16 keywords="mime magic file",
17 license="MIT",
18 test_suite='test',
19 classifiers=[
20 'Intended Audience :: Developers',
21 'License :: OSI Approved :: MIT License',
22 'Programming Language :: Python',
23 'Programming Language :: Python :: 2',
24 'Programming Language :: Python :: 3',
25 ],
26 )
0 [python-magic]
1 Depends: libmagic1
2 Conflicts: python-magic
(New empty file)
0 #!/bin/sh
1
2 set -e
3
4 # ensure we can use unicode filenames in the test
5 export LC_ALL=en_US.UTF-8
6 THISDIR=`dirname $0`
7 export PYTHONPATH=${THISDIR}/..
8
9 python2.6 ${THISDIR}/test.py
10 python2.7 ${THISDIR}/test.py
11 python3 ${THISDIR}/test.py
0 import os, sys
1 # for output which reports a local time
2 os.environ['TZ'] = 'GMT'
3 import shutil
4 import os.path
5 import unittest
6
7 import magic
8
9 class MagicTest(unittest.TestCase):
10 TESTDATA_DIR = os.path.join(os.path.dirname(__file__), 'testdata')
11
12 def assert_values(self, m, expected_values):
13 for filename, expected_value in expected_values.items():
14 try:
15 filename = os.path.join(self.TESTDATA_DIR, filename)
16 except TypeError:
17 filename = os.path.join(self.TESTDATA_DIR.encode('utf-8'), filename)
18
19
20 if type(expected_value) is not tuple:
21 expected_value = (expected_value,)
22
23 for i in expected_value:
24 with open(filename, 'rb') as f:
25 buf_value = m.from_buffer(f.read())
26
27 file_value = m.from_file(filename)
28 if buf_value == i and file_value == i:
29 break
30 else:
31 self.assertTrue(False, "no match for " + repr(expected_value))
32
33 def test_mime_types(self):
34 dest = os.path.join(MagicTest.TESTDATA_DIR, b'\xce\xbb'.decode('utf-8'))
35 shutil.copyfile(os.path.join(MagicTest.TESTDATA_DIR, 'lambda'), dest)
36 try:
37 m = magic.Magic(mime=True)
38 self.assert_values(m, {
39 'magic.pyc': 'application/octet-stream',
40 'test.pdf': 'application/pdf',
41 'test.gz': 'application/gzip',
42 'text.txt': 'text/plain',
43 b'\xce\xbb'.decode('utf-8'): 'text/plain',
44 b'\xce\xbb': 'text/plain',
45 })
46 finally:
47 os.unlink(dest)
48
49 def test_descriptions(self):
50 m = magic.Magic()
51 os.environ['TZ'] = 'UTC' # To get the last modified date of test.gz in UTC
52 try:
53 self.assert_values(m, {
54 'magic.pyc': 'python 2.4 byte-compiled',
55 'test.pdf': 'PDF document, version 1.2',
56 'test.gz':
57 ('gzip compressed data, was "test", from Unix, last modified: Sun Jun 29 01:32:52 2008',
58 'gzip compressed data, was "test", last modified: Sun Jun 29 01:32:52 2008, from Unix'),
59 'text.txt': 'ASCII text',
60 })
61 finally:
62 del os.environ['TZ']
63
64 def test_mime_encodings(self):
65 m = magic.Magic(mime_encoding=True)
66 self.assert_values(m, {
67 'text-iso8859-1.txt': 'iso-8859-1',
68 'text.txt': 'us-ascii',
69 })
70
71 def test_errors(self):
72 m = magic.Magic()
73 self.assertRaises(IOError, m.from_file, 'nonexistent')
74 self.assertRaises(magic.MagicException, magic.Magic,
75 magic_file='nonexistent')
76 os.environ['MAGIC'] = 'nonexistent'
77 try:
78 self.assertRaises(magic.MagicException, magic.Magic)
79 finally:
80 del os.environ['MAGIC']
81
82 def test_keep_going(self):
83 filename = os.path.join(self.TESTDATA_DIR, 'keep-going.jpg')
84
85 m = magic.Magic(mime=True)
86 self.assertEqual(m.from_file(filename), 'image/jpeg')
87
88 m = magic.Magic(mime=True, keep_going=True)
89 self.assertEqual(m.from_file(filename), 'image/jpeg')
90
91
92 def test_rethrow(self):
93 old = magic.magic_buffer
94 try:
95 def t(x,y):
96 raise magic.MagicException("passthrough")
97 magic.magic_buffer = t
98
99 self.assertRaises(magic.MagicException, magic.from_buffer, "hello", True)
100 finally:
101 magic.magic_buffer = old
102 if __name__ == '__main__':
103 unittest.main()
Binary diff not shown
Binary diff not shown
0 %PDF-1.2
1 7 0 obj
2 [5 0 R/XYZ 111.6 757.86]
3 endobj
4 13 0 obj
5 <<
6 /Title(About this document)
7 /A<<
8 /S/GoTo
9 /D(subsection.1.1)
10 >>
11 /Parent 12 0 R
12 /Next 14 0 R
13 >>
14 endobj
15 15 0 obj
16 <<
17 /Title(Compiling with GHC)
18 /A<<
19 /S/GoTo
20 /D(subsubsection.1.2.1)
21 >>
22 /Parent 14 0 R
23 /Next 16 0 R
24 >>
25 endobj
26 16 0 obj
27 <<
28 /Title(Compiling with Hugs)
29 /A<<
30 /S/GoTo
31 /D(subsubsection.1.2.2)
32 >>
33 /Parent 14 0 R
34 /Prev 15 0 R
35 >>
36 endobj
37 14 0 obj
38 <<
39 /Title(Compatibility)
40 /A<<
41 /S/GoTo
42 /D(subsection.1.2)
43 >>
44 /Parent 12 0 R
45 /Prev 13 0 R
46 /First 15 0 R
47 /Last 16 0 R
48 /Count -2
49 /Next 17 0 R
50 >>
51 endobj
52 17 0 obj
53 <<
54 /Title(Reporting bugs)
55 /A<<
56 /S/GoTo
57 /D(subsection.1.3)
58 >>
59 /Parent 12 0 R
60 /Prev 14 0 R
61 /Next 18 0 R
62 >>
63 endobj
64 18 0 obj
65 <<
66 /Title(History)
67 /A<<
68 /S/GoTo
69 /D(subsection.1.4)
70 >>
71 /Parent 12 0 R
72 /Prev 17 0 R
73 /Next 19 0 R
74 >>
75 endobj
76 19 0 obj
77 <<
78 /Title(License)
79 /A<<
80 /S/GoTo
81 /D(subsection.1.5)
82 >>
83 /Parent 12 0 R
84 /Prev 18 0 R
85 >>
86 endobj
87 12 0 obj
88 <<
89 /Title(Introduction)
90 /A<<
91 /S/GoTo
92 /D(section.1)
93 >>
94 /Parent 11 0 R
95 /First 13 0 R
96 /Last 19 0 R
97 /Count -5
98 /Next 20 0 R
99 >>
100 endobj
101 21 0 obj
102 <<
103 /Title(Running a parser)
104 /A<<
105 /S/GoTo
106 /D(subsection.2.1)
107 >>
108 /Parent 20 0 R
109 /Next 22 0 R
110 >>
111 endobj
112 22 0 obj
113 <<
114 /Title(Sequence and choice)
115 /A<<
116 /S/GoTo
117 /D(subsection.2.2)
118 >>
119 /Parent 20 0 R
120 /Prev 21 0 R
121 /Next 23 0 R
122 >>
123 endobj
124 23 0 obj
125 <<
126 /Title(Predictive parsers)
127 /A<<
128 /S/GoTo
129 /D(subsection.2.3)
130 >>
131 /Parent 20 0 R
132 /Prev 22 0 R
133 /Next 24 0 R
134 >>
135 endobj
136 24 0 obj
137 <<
138 /Title(Adding semantics)
139 /A<<
140 /S/GoTo
141 /D(subsection.2.4)
142 >>
143 /Parent 20 0 R
144 /Prev 23 0 R
145 /Next 25 0 R
146 >>
147 endobj
148 25 0 obj
149 <<
150 /Title(Sequences and seperators)
151 /A<<
152 /S/GoTo
153 /D(subsection.2.5)
154 >>
155 /Parent 20 0 R
156 /Prev 24 0 R
157 /Next 26 0 R
158 >>
159 endobj
160 26 0 obj
161 <<
162 /Title(Improving error messages)
163 /A<<
164 /S/GoTo
165 /D(subsection.2.6)
166 >>
167 /Parent 20 0 R
168 /Prev 25 0 R
169 /Next 27 0 R
170 >>
171 endobj
172 27 0 obj
173 <<
174 /Title(Expressions)
175 /A<<
176 /S/GoTo
177 /D(subsection.2.7)
178 >>
179 /Parent 20 0 R
180 /Prev 26 0 R
181 /Next 28 0 R
182 >>
183 endobj
184 28 0 obj
185 <<
186 /Title(Lexical analysis)
187 /A<<
188 /S/GoTo
189 /D(subsection.2.8)
190 >>
191 /Parent 20 0 R
192 /Prev 27 0 R
193 /Next 29 0 R
194 >>
195 endobj
196 30 0 obj
197 <<
198 /Title(Lexeme parsers
0 This is a web page encoded in iso-8859-1
1 יטאשפגןמ
0 Hello, World!
1