Skip to content

Commit

Permalink
wip: bison
Browse files Browse the repository at this point in the history
  • Loading branch information
dcbaker committed Aug 14, 2024
1 parent 61c7840 commit 825ddfb
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 18 deletions.
18 changes: 17 additions & 1 deletion docs/markdown/Codegen-module.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ This requires an input file, which may be a string, File, or generated source. I
- `source`: the name of the source output. If this is unset Meson will use `{base}.{ext}` with an extension of `cpp` if the input has an extension of `.ll` or `c` otherwise, with base being determined by the `plainname` argument.
- `header`: The optional output name for a header file. If this is unset no header is added
- `table`: The optional output name for a table file. If this is unset no table will be generated

'POSIX specification does not require that header output be cont')
The outputs will be in the form `source [header] [table]`, which means those can be accessed by indexing the output of the `lex` call:

```meson
Expand All @@ -57,6 +57,22 @@ l2 = codegen.lex('lexer.l', table : '@[email protected]')
table = l2[1]
```

### find_yacc()

```meson
codegen.find_yacc(implementations : ['bison', 'win_bison'])
```

This function provides fine grained controls over which implementation(s) and version(s) of the parser generator to use.

Accepts the following keyword arguments:

- `implementations`: a string array of acceptable implementations to use. May include: `yacc`, `byacc`, or `bison`.
- `yacc_version`: a string array of version constraints to apply to the `yacc` binary
- `byacc_version`: a string array of version constraints to apply to the `byacc` binary
- `bison_version`: a string array of version constraints to apply to the `bison` binary
- `win_bison_version`: a string array of version constraints to apply to the `win_bison` binary

### yacc()

```meson
Expand Down
91 changes: 74 additions & 17 deletions mesonbuild/modules/codegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from ..interpreterbase import (
ContainerTypeInfo, KwargInfo, typed_pos_args, typed_kwargs, noPosargs
)
from ..mesonlib import File, MesonException, Popen_safe
from ..mesonlib import File, MesonException, Popen_safe, version_compare
from ..programs import ExternalProgram
from .. import mlog

Expand All @@ -27,6 +27,7 @@

Program: TypeAlias = T.Union[Executable, ExternalProgram, OverrideProgram]
LexImpls = Literal['lex', 'flex', 'reflex', 'win_flex']
YaccImpls = Literal['yacc', 'byacc', 'bison', 'win_bison']

class LexKwargs(TypedDict):

Expand All @@ -47,12 +48,19 @@ class FindLexKwargs(TypedDict):
class YaccKwargs(TypedDict):

args: T.List[str]
version: T.List[str]
source: T.Optional[str]
header: T.Optional[str]
locations: T.Optional[str]
plainname: bool

class FindYaccKwargs(TypedDict):

yacc_version: T.List[str]
byacc_version: T.List[str]
bison_version: T.List[str]
win_bison_version: T.List[str]
implementations: T.List[YaccImpls]


def is_subset_validator(choices: T.Set[str]) -> T.Callable[[T.List[str]], T.Optional[str]]:

Expand Down Expand Up @@ -88,6 +96,7 @@ def __init__(self, interpreter: Interpreter) -> None:
self.methods.update({
'find_lex': self.find_lex_method,
'lex': self.lex_method,
'find_yacc': self.find_yacc_method,
'yacc': self.yacc_method,
})

Expand Down Expand Up @@ -223,43 +232,91 @@ def lex_method(self, state: ModuleState, args: T.Tuple[T.Union[str, File, Genera

return ModuleReturnValue(target, [target])

def __find_yacc(self, state: ModuleState, version: T.List[str]) -> None:
if 'yacc' in self._generators:
return

assert state.environment.machines.host is not None, 'for mypy'
names: T.List[str]
if state.environment.machines.host.system == 'windows':
names = ['win_bison', 'bison', 'yacc']
def __find_yacc(self, state: ModuleState,
yacc_version: T.Optional[T.List[str]] = None,
byacc_version: T.Optional[T.List[str]] = None,
bison_version: T.Optional[T.List[str]] = None,
win_bison_version: T.Optional[T.List[str]] = None,
implementations: T.Optional[T.List[YaccImpls]] = None) -> None:
names: T.List[YaccImpls]
if implementations:
names = implementations
else:
names = ['bison', 'byacc', 'yacc']
assert state.environment.machines.host is not None, 'for mypy'
if state.environment.machines.host.system == 'windows':
names = ['win_bison', 'bison', 'yacc']
else:
names = ['bison', 'byacc', 'yacc']

versions: T.Mapping[YaccImpls, T.List[str]] = {
'yacc': yacc_version or [],
'byacc': byacc_version or [],
'bison': bison_version or [],
'win_bison': win_bison_version or [],
}

for name in names:
bin = state.find_program(names, wanted=version, required=name == names[-1])
bin = state.find_program(name, wanted=versions[name], required=name == names[-1])
if bin.found():
break

args: T.List[str] = ['@INPUT@', '-o', '@OUTPUT0@']
# TODO: Determine if "yacc" is "bison" or "byacc"
if bin.name == 'bison':
# TODO: add --color=always when appropriate

impl = T.cast('YaccImpls', bin.name)
if impl == 'yacc' and isinstance(bin, ExternalProgram):
_, out, _ = Popen_safe(bin.get_command() + ['--version'])
if 'GNU Bison' in out:
impl = 'bison'
elif out.startswith('yacc - 2'):
impl = 'byacc'

if impl in {'bison', 'win_bison'}:
args.append('--defines=@OUTPUT1@')
else:
if isinstance(bin, ExternalProgram) and version_compare(bin.get_version(), '>= 3.4'):
args.append('--color=always')
elif impl == 'byacc':
args.extend(['-H', '@OUTPUT1@'])
else:
mlog.warning('This yacc does not appear to be bison or byacc, the '
'POSIX specification does not require that header '
'output location be configurable, and may not work.',
fatal=False)
args.append('-H')
self._generators['yacc'] = Generator(bin, T.cast('ImmutableListProtocol[str]', args))

@noPosargs
@typed_kwargs(
'codegen.find_yacc',
KwargInfo('yacc_version', ContainerTypeInfo(list, str), default=[], listify=True),
KwargInfo('byacc_version', ContainerTypeInfo(list, str), default=[], listify=True),
KwargInfo('bison_version', ContainerTypeInfo(list, str), default=[], listify=True),
KwargInfo('win_bison_version', ContainerTypeInfo(list, str), default=[], listify=True),
KwargInfo(
'implementations',
ContainerTypeInfo(list, str),
default=[],
listify=True,
validator=is_subset_validator({'yacc', 'byacc', 'bison', 'win_bison'})
),
)
def find_yacc_method(self, state: ModuleState, args: T.Tuple, kwargs: FindYaccKwargs) -> None:
if 'yacc' in self._generators:
raise MesonException('Cannot call CodeGen.find_yacc() twice, or after CodeGen.yacc() has been called')
self.__find_yacc(state, **kwargs)

@typed_pos_args('codegen.yacc', (str, File, GeneratedList, CustomTarget, CustomTargetIndex))
@typed_kwargs(
'codegen.yacc',
KwargInfo('version', ContainerTypeInfo(list, str), default=[], listify=True),
KwargInfo('args', ContainerTypeInfo(list, str), default=[], listify=True),
KwargInfo('source', (str, NoneType)),
KwargInfo('header', (str, NoneType)),
KwargInfo('locations', (str, NoneType)),
KwargInfo('plainname', bool, default=False),
)
def yacc_method(self, state: ModuleState, args: T.Tuple[T.Union[str, File, CustomTarget, CustomTargetIndex, GeneratedList]], kwargs: YaccKwargs) -> ModuleReturnValue:
self.__find_yacc(state, kwargs['version'])
if 'yacc' not in self._generators:
self.__find_yacc(state)

input = state._interpreter.source_strings_to_files([args[0]])[0]
if isinstance(input, File):
Expand Down
1 change: 1 addition & 0 deletions test cases/frameworks/8 flex/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ endif
codegen = import('unstable-codegen')
codegen.find_lex(implementations : ['lex', 'flex', 'reflex'])
lfiles = codegen.lex('lexer.l')
codegen.find_yacc(implementations : ['byacc', 'bison', 'yacc'])
pfiles = codegen.yacc('parser.y', header : '@[email protected]')

e = executable(
Expand Down

0 comments on commit 825ddfb

Please sign in to comment.