fix: noconfirm auto-selects first AUR match
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
# Examples in C
|
||||
|
||||
This directory contains simple examples of Bison grammar files in C.
|
||||
|
||||
Some of them come from the documentation, which should be installed together
|
||||
with Bison. The URLs are provided for convenience.
|
||||
|
||||
## rpcalc - Reverse Polish Notation Calculator
|
||||
The first example is that of a simple double-precision Reverse Polish
|
||||
Notation calculator (a calculator using postfix operators). This example
|
||||
provides a good starting point, since operator precedence is not an issue.
|
||||
|
||||
Extracted from the documentation: [Reverse Polish Notation
|
||||
Calculator](https://www.gnu.org/software/bison/manual/html_node/RPN-Calc.html).
|
||||
|
||||
## calc - Simple Calculator
|
||||
This example is slightly more complex than rpcalc: it features infix
|
||||
operators (`1 + 2`, instead of `1 2 +` in rpcalc), but it does so using a
|
||||
unambiguous grammar of the arithmetic instead of using precedence
|
||||
directives (%left, etc.).
|
||||
|
||||
## mfcalc - Multi-Function Calculator
|
||||
A more complete C example: a multi-function calculator. More complex than
|
||||
the previous example. Using precedence directives to support infix
|
||||
operators.
|
||||
|
||||
Extracted from the documentation: [Multi-Function Calculator:
|
||||
mfcalc](https://www.gnu.org/software/bison/manual/html_node/Multi_002dfunction-Calc.html).
|
||||
|
||||
## lexcalc - calculator with Flex and Bison
|
||||
The calculator with precedence directives and location tracking. It uses
|
||||
Flex to generate the scanner.
|
||||
|
||||
## reccalc - recursive calculator with Flex and Bison
|
||||
This example builds on top of the previous one to provide a reentrant
|
||||
parser. Such parsers can be called concurrently in different threads, or
|
||||
even recursively. To demonstrate this feature, expressions in parentheses
|
||||
are tokenized as strings, and then recursively parsed from the parser. So
|
||||
`(((1)+(2))*((3)+(4)))` uses eight parsers, with a depth of four.
|
||||
|
||||
## pushcalc - calculator implemented with a push parser
|
||||
All the previous examples are so called "pull parsers": the user invokes the
|
||||
parser once, which repeatedly calls the scanner until the input is drained.
|
||||
|
||||
This example demonstrates the "push parsers": the user calls the scanner to
|
||||
fetch the next token, passes it to the parser, and repeats the operation
|
||||
until the input is drained.
|
||||
|
||||
This example is a straightforward conversion of the 'calc' example to the
|
||||
push-parser model.
|
||||
|
||||
## bistromathic - all the bells and whistles
|
||||
This example demonstrates best practices when using Bison.
|
||||
- Its hand-written scanner tracks locations.
|
||||
- Its interface is pure.
|
||||
- It uses %params to pass user information to the parser and scanner.
|
||||
- Its scanner uses the `error` token to signal lexical errors and enter
|
||||
error recovery.
|
||||
- Its interface is "incremental", well suited for interaction: it uses the
|
||||
push-parser API to feed the parser with the incoming tokens.
|
||||
- It features an interactive command line with completion based on the
|
||||
parser state, based on `yyexpected_tokens`.
|
||||
- It uses Bison's standard catalog for internationalization of generated
|
||||
messages.
|
||||
- It uses a custom syntax error with location, lookahead correction and
|
||||
token internationalization.
|
||||
- Error messages quote the source with squiggles that underline the error:
|
||||
```
|
||||
> 123 456
|
||||
1.5-7: syntax error: expected end of file or + or - or * or / or ^ before number
|
||||
1 | 123 456
|
||||
| ^~~
|
||||
```
|
||||
- It supports debug traces with semantic values.
|
||||
- It uses named references instead of the traditional $1, $2, etc.
|
||||
|
||||
<!---
|
||||
|
||||
Local Variables:
|
||||
fill-column: 76
|
||||
ispell-dictionary: "american"
|
||||
End:
|
||||
|
||||
Copyright (C) 2018-2021 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of GNU bison, the GNU Compiler Compiler.
|
||||
|
||||
Permission is granted to copy, distribute and/or modify this document
|
||||
under the terms of the GNU Free Documentation License, Version 1.3 or
|
||||
any later version published by the Free Software Foundation; with no
|
||||
Invariant Sections, with no Front-Cover Texts, and with no Back-Cover
|
||||
Texts. A copy of the license is included in the "GNU Free
|
||||
Documentation License" file as part of this distribution.
|
||||
|
||||
LocalWords: mfcalc calc parsers yy rpcalc lexcalc redux reccalc ispell
|
||||
LocalWords: reentrant tokenized american postfix pushcalc bistromathic
|
||||
LocalWords: lookahead
|
||||
|
||||
-->
|
||||
@@ -0,0 +1,35 @@
|
||||
# This Makefile is designed to be simple and readable. It does not
|
||||
# aim at portability. It requires GNU Make.
|
||||
|
||||
BASE = bistromathic
|
||||
BISON = bison
|
||||
|
||||
# We need to find the headers and libs for readline (and possibly intl).
|
||||
# You probably need to customize this for your own environment.
|
||||
CPPFLAGS = -I/opt/local/include
|
||||
LDFLAGS = -L/opt/local/lib
|
||||
|
||||
# Find the translation catalog for Bison's generated messagess.
|
||||
BISON_LOCALEDIR = $(shell $(BISON) $(BISON_FLAGS) --print-localedir)
|
||||
CPPFLAGS += -DENABLE_NLS -DBISON_LOCALEDIR='"$(BISON_LOCALEDIR)"'
|
||||
|
||||
LIBS = -lreadline -lm # In some environments, -lintl is needed.
|
||||
|
||||
all: $(BASE)
|
||||
|
||||
%.c %.h %.html %.xml %.gv: %.y
|
||||
$(BISON) $(BISONFLAGS) --header --html --graph -o $*.c $<
|
||||
|
||||
$(BASE): parse.o
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS)
|
||||
|
||||
run: $(BASE)
|
||||
@echo "Type bistromathic expressions. Quit with ctrl-d."
|
||||
./$<
|
||||
|
||||
CLEANFILES = \
|
||||
$(BASE) *.o \
|
||||
parse.[ch] parse.output parse.xml parse.html parse.gv
|
||||
|
||||
clean:
|
||||
rm -f $(CLEANFILES)
|
||||
@@ -0,0 +1,49 @@
|
||||
# bistromathic - all the bells and whistles
|
||||
This example demonstrates best practices when using Bison.
|
||||
- Its hand-written scanner tracks locations.
|
||||
- Its interface is pure.
|
||||
- It uses %params to pass user information to the parser and scanner.
|
||||
- Its scanner uses the `error` token to signal lexical errors and enter
|
||||
error recovery.
|
||||
- Its interface is "incremental", well suited for interaction: it uses the
|
||||
push-parser API to feed the parser with the incoming tokens.
|
||||
- It features an interactive command line with completion based on the
|
||||
parser state, based on `yyexpected_tokens`.
|
||||
- It uses Bison's standard catalog for internationalization of generated
|
||||
messages.
|
||||
- It uses a custom syntax error with location, lookahead correction and
|
||||
token internationalization.
|
||||
- Error messages quote the source with squiggles that underline the error:
|
||||
```
|
||||
> 123 456
|
||||
1.5-7: syntax error: expected end of file or + or - or * or / or ^ before number
|
||||
1 | 123 456
|
||||
| ^~~
|
||||
```
|
||||
- It supports debug traces with semantic values.
|
||||
- It uses named references instead of the traditional $1, $2, etc.
|
||||
|
||||
To customize the interaction with bistromathic, see the GNU Readline user
|
||||
manual (see `info rluserman`).
|
||||
|
||||
<!---
|
||||
Local Variables:
|
||||
fill-column: 76
|
||||
ispell-dictionary: "american"
|
||||
End:
|
||||
|
||||
Copyright (C) 2020-2021 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of Bison, the GNU Compiler Compiler.
|
||||
|
||||
Permission is granted to copy, distribute and/or modify this document
|
||||
under the terms of the GNU Free Documentation License, Version 1.3 or
|
||||
any later version published by the Free Software Foundation; with no
|
||||
Invariant Sections, with no Front-Cover Texts, and with no Back-Cover
|
||||
Texts. A copy of the license is included in the "GNU Free
|
||||
Documentation License" file as part of this distribution.
|
||||
|
||||
LocalWords: bistromathic yyexpected lookahead ispell american
|
||||
LocalWords: MERCHANTABILITY
|
||||
|
||||
--->
|
||||
@@ -0,0 +1,368 @@
|
||||
#! /bin/sh
|
||||
|
||||
# Copyright (C) 2020-2021 Free Software Foundation, Inc.
|
||||
#
|
||||
# This program 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.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
# Users may customize the behavior of readline, which might break our
|
||||
# expected results.
|
||||
INPUTRC=/dev/null
|
||||
export INPUTRC
|
||||
|
||||
# Beware of portability issues of readline when not feeding it from a
|
||||
# terminal.
|
||||
#
|
||||
# With recent versions of GNU Readline, input "1+2*3\n" gives
|
||||
# "> 1+2*3\n7\n> \n"
|
||||
#
|
||||
# macOS' version does not display the prompt and does not repeat stdin
|
||||
# on stdout, so input "1+2*3\n" gives "7\n" as output. Let's try to
|
||||
# cope with this.
|
||||
#
|
||||
# On OpenBSD 6.5 the prompt is displayed, but the input is not
|
||||
# repeated (!). So input "1+2*3\n" gives "> 7\n> \n" as output.
|
||||
#
|
||||
# On AIX, you get some escaping sequence before the prompt:
|
||||
# "<ESC>[?1034h> 1+2*3". It appears to pass the terminfo capability
|
||||
# "smm", to put the terminal in "meta mode": as if the user had hit
|
||||
# META.
|
||||
|
||||
echo >perfect '> 0
|
||||
0
|
||||
> '
|
||||
|
||||
echo >ok '0'
|
||||
echo '0' | prog >effective
|
||||
|
||||
echo "checking for readline output..."
|
||||
if diff perfect effective; then
|
||||
# Alles ist gut.
|
||||
strip_prompt=false
|
||||
elif diff ok effective; then
|
||||
strip_prompt=true
|
||||
else
|
||||
skip "this is not the GNU Readline we expect"
|
||||
fi
|
||||
|
||||
|
||||
cat >input <<EOF
|
||||
1+2*3
|
||||
EOF
|
||||
run 0 '> 1+2*3
|
||||
7
|
||||
> '
|
||||
|
||||
cat >input <<EOF
|
||||
(1+2) * 3
|
||||
EOF
|
||||
run 0 '> (1+2) * 3
|
||||
9
|
||||
> '
|
||||
run -noerr 0 '> (1+2) * 3
|
||||
9
|
||||
> ' -p
|
||||
|
||||
cat >input <<EOF
|
||||
a = 256
|
||||
sqrt (a)
|
||||
EOF
|
||||
run 0 '> a = 256
|
||||
256
|
||||
> sqrt (a)
|
||||
16
|
||||
> '
|
||||
|
||||
cat >input <<EOF
|
||||
a = .16
|
||||
b = 10 ^ 2
|
||||
sqrt (a * b)
|
||||
EOF
|
||||
run 0 '> a = .16
|
||||
0.16
|
||||
> b = 10 ^ 2
|
||||
100
|
||||
> sqrt (a * b)
|
||||
4
|
||||
> '
|
||||
|
||||
cat >input <<EOF
|
||||
*
|
||||
EOF
|
||||
run 0 '> *
|
||||
> ''
|
||||
err: 1.1: syntax error: expected end of file or - or ( or exit or number or function etc., before *
|
||||
err: 1 | *
|
||||
err: | ^'
|
||||
|
||||
# Underline long errors.
|
||||
cat >input <<EOF
|
||||
123 123456
|
||||
EOF
|
||||
run 0 '> 123 123456
|
||||
> ''
|
||||
err: 1.5-10: syntax error: expected end of file or + or - or * or / or ^ before number
|
||||
err: 1 | 123 123456
|
||||
err: | ^~~~~~'
|
||||
|
||||
cat >input <<EOF
|
||||
1 + 2 * * 3
|
||||
EOF
|
||||
run 0 '> 1 + 2 * * 3
|
||||
> ''
|
||||
err: 1.9: syntax error: expected - or ( or number or function or variable before *
|
||||
err: 1 | 1 + 2 * * 3
|
||||
err: | ^'
|
||||
|
||||
cat >input <<EOF
|
||||
1 / 0
|
||||
EOF
|
||||
run 0 '> 1 / 0
|
||||
> ''
|
||||
err: 1.1-5: error: division by zero'
|
||||
|
||||
|
||||
## ---------------- ##
|
||||
## Error recovery. ##
|
||||
## ---------------- ##
|
||||
|
||||
cat >input <<EOF
|
||||
((1 ++ 2) ** 3)
|
||||
(1 ++ 2) + (3 ** 4)
|
||||
EOF
|
||||
run 0 '> ((1 ++ 2) ** 3)
|
||||
666
|
||||
> (1 ++ 2) + (3 ** 4)
|
||||
1332
|
||||
> ''
|
||||
err: 1.6: syntax error: expected - or ( or number or function or variable before +
|
||||
err: 1 | ((1 ++ 2) ** 3)
|
||||
err: | ^
|
||||
err: 2.5: syntax error: expected - or ( or number or function or variable before +
|
||||
err: 2 | (1 ++ 2) + (3 ** 4)
|
||||
err: | ^
|
||||
err: 2.16: syntax error: expected - or ( or number or function or variable before *
|
||||
err: 2 | (1 ++ 2) + (3 ** 4)
|
||||
err: | ^'
|
||||
|
||||
# The rule "( error )" should work even if there are no tokens between "(" and ")".
|
||||
cat >input <<EOF
|
||||
()
|
||||
EOF
|
||||
run 0 '> ()
|
||||
666
|
||||
> ''
|
||||
err: 1.2: syntax error: expected - or ( or number or function or variable before )
|
||||
err: 1 | ()
|
||||
err: | ^'
|
||||
|
||||
|
||||
cat >input <<EOF
|
||||
100% + 10
|
||||
EOF
|
||||
run 0 '> 100% + 10
|
||||
> ''
|
||||
err: 1.4: syntax error: invalid character: %'
|
||||
|
||||
# Traces. This allows to check the location of the error. If we
|
||||
# forget to map YYerror to YYUNDEF, error recovery enters an endless
|
||||
# loop with this input.
|
||||
cat >input <<EOF
|
||||
(+_)
|
||||
EOF
|
||||
run 0 '> (+_)
|
||||
666
|
||||
> ''
|
||||
err: Starting parse
|
||||
err: Entering state 0
|
||||
err: Stack now 0
|
||||
err: Reading a token
|
||||
err: Next token is token ( (1.1: )
|
||||
err: Shifting token ( (1.1: )
|
||||
err: Entering state 2
|
||||
err: Stack now 0 2
|
||||
err: Return for a new token:
|
||||
err: Reading a token
|
||||
err: Next token is token + (1.2: )
|
||||
err: LAC: initial context established for +
|
||||
err: LAC: checking lookahead +: Err
|
||||
err: LAC: checking lookahead end of file: Err
|
||||
err: LAC: checking lookahead +: Err
|
||||
err: LAC: checking lookahead -: S1
|
||||
err: LAC: checking lookahead *: Err
|
||||
err: LAC: checking lookahead /: Err
|
||||
err: LAC: checking lookahead ^: Err
|
||||
err: LAC: checking lookahead (: S2
|
||||
err: LAC: checking lookahead ): Err
|
||||
err: LAC: checking lookahead =: Err
|
||||
err: LAC: checking lookahead exit: Err
|
||||
err: LAC: checking lookahead number: S4
|
||||
err: LAC: checking lookahead function: S5
|
||||
err: LAC: checking lookahead variable: S6
|
||||
err: LAC: checking lookahead NEG: Err
|
||||
err: 1.2: syntax error: expected - or ( or number or function or variable before +
|
||||
err: 1 | (+_)
|
||||
err: | ^
|
||||
err: LAC: initial context discarded due to error recovery
|
||||
err: Shifting token error (1.2: )
|
||||
err: Entering state 10
|
||||
err: Stack now 0 2 10
|
||||
err: Next token is token + (1.2: )
|
||||
err: LAC: initial context established for +
|
||||
err: LAC: checking lookahead +: Err
|
||||
err: Error: discarding token + (1.2: )
|
||||
err: Error: popping token error (1.2: )
|
||||
err: Stack now 0 2
|
||||
err: LAC: initial context discarded due to error recovery
|
||||
err: Shifting token error (1.2: )
|
||||
err: Entering state 10
|
||||
err: Stack now 0 2 10
|
||||
err: Return for a new token:
|
||||
err: 1.3: syntax error: invalid character: _
|
||||
err: Reading a token
|
||||
err: Error: popping token error (1.2: )
|
||||
err: Stack now 0 2
|
||||
err: Shifting token error (1.2-3: )
|
||||
err: Entering state 10
|
||||
err: Stack now 0 2 10
|
||||
err: Next token is token invalid token (1.3: )
|
||||
err: LAC: initial context established for invalid token
|
||||
err: LAC: checking lookahead invalid token: Always Err
|
||||
err: Error: discarding token invalid token (1.3: )
|
||||
err: Error: popping token error (1.2-3: )
|
||||
err: Stack now 0 2
|
||||
err: LAC: initial context discarded due to error recovery
|
||||
err: Shifting token error (1.2-3: )
|
||||
err: Entering state 10
|
||||
err: Stack now 0 2 10
|
||||
err: Return for a new token:
|
||||
err: Reading a token
|
||||
err: Next token is token ) (1.4: )
|
||||
err: Shifting token ) (1.4: )
|
||||
err: Entering state 20
|
||||
err: Stack now 0 2 10 20
|
||||
err: Reducing stack by rule XX (line XXX):
|
||||
err: $1 = token ( (1.1: )
|
||||
err: $2 = token error (1.2-3: )
|
||||
err: $3 = token ) (1.4: )
|
||||
err: -> $$ = nterm exp (1.1-4: 666)
|
||||
err: Entering state 8
|
||||
err: Stack now 0 8
|
||||
err: Return for a new token:
|
||||
err: Reading a token
|
||||
err: Now at end of input.
|
||||
err: LAC: initial context established for end of file
|
||||
err: LAC: checking lookahead end of file: R2 G7 S14
|
||||
err: Reducing stack by rule XX (line XXX):
|
||||
err: $1 = nterm exp (1.1-4: 666)
|
||||
err: -> $$ = nterm input (1.1-4: )
|
||||
err: Entering state 7
|
||||
err: Stack now 0 7
|
||||
err: Now at end of input.
|
||||
err: Shifting token end of file (1.5: )
|
||||
err: LAC: initial context discarded due to shift
|
||||
err: Entering state 14
|
||||
err: Stack now 0 7 14
|
||||
err: Stack now 0 7 14
|
||||
err: Cleanup: popping token end of file (1.5: )
|
||||
err: Cleanup: popping nterm input (1.1-4: )' -p
|
||||
|
||||
|
||||
|
||||
## ------------ ##
|
||||
## Completion. ##
|
||||
## ------------ ##
|
||||
|
||||
# From now on, the differences between versions of GNU Readline are
|
||||
# too painful to try to cope with.
|
||||
if $strip_prompt; then
|
||||
echo "SKIP: this is not the GNU Readline we expect"
|
||||
exit $status
|
||||
fi
|
||||
|
||||
# On Windows10/MSYS2 the ^G coming from <tab> completion is not
|
||||
# emitted the same way
|
||||
# (https://lists.gnu.org/r/bug-bison/2020-05/msg00076.html).
|
||||
echo "checking for kernel name... $(uname -s)"
|
||||
case $(uname -s) in
|
||||
(MSYS*)
|
||||
echo "SKIP: this is Windows/MSYS"
|
||||
exit $status
|
||||
;;
|
||||
esac
|
||||
|
||||
|
||||
# Check completion after an operator.
|
||||
sed -e 's/\\t/ /g' >input <<EOF
|
||||
(1+\t\t
|
||||
EOF
|
||||
# Nuke the possible trailing white spaces in the effective output.
|
||||
# This is to cope with some readlines that pad all the suggestions
|
||||
# with white spaces (for alignment), including the last one on a line.
|
||||
#
|
||||
# See for instance <https://trac.macports.org/ticket/59927#comment:48>
|
||||
# and its test-suite.log:
|
||||
# <https://trac.macports.org/attachment/ticket/59927/bison-3.7.6-test-10.13.test-suite.log>
|
||||
run -t 0 '> (1+
|
||||
( - atan cos exp ln number sin sqrt
|
||||
> (1+
|
||||
>
|
||||
err: 1.4: syntax error: expected - or ( or number or function or variable before end of file
|
||||
err: 1 | (1+
|
||||
err: | ^'
|
||||
|
||||
# Check the completion of a word.
|
||||
sed -e 's/\\t/ /g' >input <<EOF
|
||||
(at\t\t
|
||||
EOF
|
||||
run 0 '> (atan ( ''
|
||||
> ''
|
||||
err: 1.9: syntax error: expected - or ( or number or function or variable before end of file
|
||||
err: 1 | (atan ( ''
|
||||
err: | ^'
|
||||
|
||||
# Check the completion at the very beginning.
|
||||
sed -e 's/\\t/ /g' >input <<EOF
|
||||
e\t\t
|
||||
EOF
|
||||
run -n 0 '> e
|
||||
end of file exit exp ''
|
||||
> e
|
||||
0
|
||||
> ''
|
||||
err: '
|
||||
|
||||
# Check that completion prints valid locations even when there is an error.
|
||||
sed -e 's/\\t/ /g' >input <<EOF
|
||||
1++\t
|
||||
EOF
|
||||
run -n 0 '> 1++ ''
|
||||
> ''
|
||||
err: 1.3: syntax error: expected - or ( or number or function or variable before +
|
||||
err: 1 | 1++ ''
|
||||
err: | ^
|
||||
'
|
||||
|
||||
# And even when the error was recovered from.
|
||||
sed -e 's/\\t/ /g' >input <<EOF
|
||||
(1++2) + 3 +\t\t
|
||||
EOF
|
||||
run -n 0 '> (1++2) + 3 + ''
|
||||
> ''
|
||||
err: 1.4: syntax error: expected - or ( or number or function or variable before +
|
||||
err: 1 | (1++2) + 3 + ''
|
||||
err: | ^
|
||||
err: 1.15: syntax error: expected - or ( or number or function or variable before end of file
|
||||
err: 1 | (1++2) + 3 + ''
|
||||
err: | ^
|
||||
'
|
||||
@@ -0,0 +1,41 @@
|
||||
## Copyright (C) 2020-2021 Free Software Foundation, Inc.
|
||||
##
|
||||
## This program 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.
|
||||
##
|
||||
## This program is distributed in the hope that it will be useful,
|
||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU General Public License for more details.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License
|
||||
## along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
bistromathicdir = $(docdir)/%D%
|
||||
|
||||
## --------------- ##
|
||||
## Bistromathics. ##
|
||||
## --------------- ##
|
||||
|
||||
%D%/parse.c: $(dependencies)
|
||||
|
||||
if ENABLE_BISTROMATHIC
|
||||
check_PROGRAMS += %D%/bistromathic
|
||||
TESTS += %D%/bistromathic.test
|
||||
nodist_%C%_bistromathic_SOURCES = %D%/parse.y
|
||||
|
||||
# Don't use gnulib's system headers.
|
||||
%C%_bistromathic_CPPFLAGS = \
|
||||
-DBISON_LOCALEDIR='"$(localdir)"' \
|
||||
-DLOCALEDIR='"$(localdir)"' \
|
||||
-I$(top_srcdir)/%D% -I$(top_builddir)/%D%
|
||||
%C%_bistromathic_CFLAGS = $(TEST_CFLAGS)
|
||||
%C%_bistromathic_LDADD = -lm $(LIBREADLINE) $(LIBINTL)
|
||||
endif
|
||||
|
||||
EXTRA_DIST += %D%/bistromathic.test
|
||||
dist_bistromathic_DATA = %D%/parse.y %D%/Makefile %D%/README.md
|
||||
CLEANFILES += %D%/parse.[ch] %D%/parse.output
|
||||
CLEANDIRS += %D%/*.dSYM
|
||||
@@ -0,0 +1,695 @@
|
||||
/* Parser and scanner for bistromathic. -*- C -*-
|
||||
|
||||
Copyright (C) 2019-2021 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of Bison, the GNU Compiler Compiler.
|
||||
|
||||
This program 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.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
%require "3.7"
|
||||
|
||||
// Emitted on top of the implementation file.
|
||||
%code top {
|
||||
#include <ctype.h> // isdigit
|
||||
#include <locale.h> // LC_ALL
|
||||
#include <math.h> // cos, sin, etc.
|
||||
#include <stdarg.h> // va_start
|
||||
#include <stdio.h> // printf
|
||||
#include <stdlib.h> // calloc
|
||||
#include <string.h> // strcmp
|
||||
|
||||
#include <readline/readline.h>
|
||||
#include <readline/history.h>
|
||||
|
||||
#if defined ENABLE_NLS && ENABLE_NLS
|
||||
// Unable the translation of Bison's generated messages.
|
||||
# define YYENABLE_NLS 1
|
||||
# include <libintl.h>
|
||||
// Unless specified otherwise, we expect bistromathic's own
|
||||
// catalog to be installed in the same tree as Bison's catalog.
|
||||
# ifndef LOCALEDIR
|
||||
# define LOCALEDIR BISON_LOCALEDIR
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
|
||||
// Emitted in the header file, before the definition of YYSTYPE.
|
||||
%code requires {
|
||||
// Function type.
|
||||
typedef double (func_t) (double);
|
||||
|
||||
// Data type for links in the chain of symbols.
|
||||
typedef struct symrec symrec;
|
||||
struct symrec
|
||||
{
|
||||
char *name; // name of symbol
|
||||
int type; // type of symbol: either VAR or FUN
|
||||
union
|
||||
{
|
||||
double var; // value of a VAR
|
||||
func_t *fun; // value of a FUN
|
||||
} value;
|
||||
symrec *next; // link field
|
||||
};
|
||||
|
||||
symrec *putsym (char const *name, int sym_type);
|
||||
symrec *getsym (char const *name);
|
||||
|
||||
// Exchanging information with the parser.
|
||||
typedef struct
|
||||
{
|
||||
// Whether to not emit error messages.
|
||||
int silent;
|
||||
// The current input line.
|
||||
const char *line;
|
||||
} user_context;
|
||||
}
|
||||
|
||||
// Emitted in the header file, after the definition of YYSTYPE.
|
||||
%code provides {
|
||||
# ifndef __attribute__
|
||||
# ifndef __GNUC__
|
||||
# define __attribute__(Spec) /* empty */
|
||||
# endif
|
||||
# endif
|
||||
|
||||
yytoken_kind_t
|
||||
yylex (const char **line, YYSTYPE *yylval, YYLTYPE *yylloc,
|
||||
const user_context *uctx);
|
||||
void yyerror (const YYLTYPE *loc, const user_context *uctx,
|
||||
char const *format, ...)
|
||||
__attribute__ ((__format__ (__printf__, 3, 4)));
|
||||
}
|
||||
|
||||
// Emitted in the implementation file.
|
||||
%code {
|
||||
// Print *LOC on OUT.
|
||||
static void location_print (FILE *out, YYLTYPE const * const loc);
|
||||
#define YYLOCATION_PRINT location_print
|
||||
|
||||
#if defined ENABLE_NLS && ENABLE_NLS
|
||||
# define _(Msgid) gettext (Msgid)
|
||||
#else
|
||||
# define _(Msgid) (Msgid)
|
||||
#endif
|
||||
|
||||
// Whether to quit.
|
||||
int done = 0;
|
||||
}
|
||||
|
||||
// Include the header in the implementation rather than duplicating it.
|
||||
%define api.header.include {"parse.h"}
|
||||
|
||||
// Don't share global variables between the scanner and the parser.
|
||||
%define api.pure full
|
||||
|
||||
// Generate a push parser.
|
||||
%define api.push-pull push
|
||||
|
||||
// To avoid name clashes (e.g., with C's EOF) prefix token definitions
|
||||
// with TOK_ (e.g., TOK_EOF).
|
||||
%define api.token.prefix {TOK_}
|
||||
|
||||
// Customized syntax error messages (see yyreport_syntax_error)...
|
||||
%define parse.error custom
|
||||
|
||||
// ... with locations...
|
||||
%locations
|
||||
|
||||
// ... and accurate list of expected tokens.
|
||||
%define parse.lac full
|
||||
|
||||
// Generate the parser description file (calc.output).
|
||||
%verbose
|
||||
|
||||
// User information exchanged with the parser and scanner.
|
||||
%param {const user_context *uctx}
|
||||
|
||||
// Generate YYSTYPE from the types assigned to symbols.
|
||||
%define api.value.type union
|
||||
%token
|
||||
PLUS "+"
|
||||
MINUS "-"
|
||||
STAR "*"
|
||||
SLASH "/"
|
||||
CARET "^"
|
||||
LPAREN "("
|
||||
RPAREN ")"
|
||||
EQUAL "="
|
||||
EXIT "exit"
|
||||
<double>
|
||||
NUM _("number")
|
||||
<symrec*>
|
||||
FUN _("function")
|
||||
VAR _("variable")
|
||||
|
||||
%nterm <double> exp
|
||||
|
||||
// Enable run-time traces (yydebug).
|
||||
%define parse.trace
|
||||
|
||||
// Formatting semantic values in debug traces.
|
||||
%printer { fprintf (yyo, "%s", $$->name); } VAR;
|
||||
%printer { fprintf (yyo, "%s()", $$->name); } FUN;
|
||||
%printer { fprintf (yyo, "%g", $$); } <double>;
|
||||
|
||||
|
||||
// Precedence (from lowest to highest) and associativity.
|
||||
%precedence "="
|
||||
%left "+" "-"
|
||||
%left "*" "/"
|
||||
%precedence NEG // negation--unary minus
|
||||
%right "^" // exponentiation
|
||||
|
||||
%% // The grammar follows.
|
||||
input:
|
||||
%empty
|
||||
| exp { printf ("%.10g\n", $exp); }
|
||||
| "exit" { done = 1; }
|
||||
;
|
||||
|
||||
exp:
|
||||
NUM
|
||||
| VAR { $$ = $VAR->value.var; }
|
||||
| VAR "=" exp { $$ = $3; $VAR->value.var = $3; }
|
||||
| FUN "(" exp ")" { $$ = $FUN->value.fun ($3); }
|
||||
| exp[l] "+" exp[r] { $$ = $l + $r; }
|
||||
| exp[l] "-" exp[r] { $$ = $l - $r; }
|
||||
| exp[l] "*" exp[r] { $$ = $l * $r; }
|
||||
| exp[l] "/" exp[r]
|
||||
{
|
||||
if ($r == 0)
|
||||
{
|
||||
yyerror (&@$, uctx, _("error: division by zero"));
|
||||
YYERROR;
|
||||
}
|
||||
else
|
||||
$$ = $l / $r;
|
||||
}
|
||||
| "-" exp %prec NEG { $$ = -$2; }
|
||||
| exp[l] "^" exp[r] { $$ = pow ($l, $r); }
|
||||
| "(" exp ")" { $$ = $2; }
|
||||
| "(" error ")" { $$ = 666; }
|
||||
;
|
||||
|
||||
// End of grammar.
|
||||
%%
|
||||
|
||||
/*------------.
|
||||
| Functions. |
|
||||
`------------*/
|
||||
|
||||
struct init
|
||||
{
|
||||
char const *name;
|
||||
func_t *fun;
|
||||
};
|
||||
|
||||
static struct init const funs[] =
|
||||
{
|
||||
{ "atan", atan },
|
||||
{ "cos", cos },
|
||||
{ "exp", exp },
|
||||
{ "ln", log },
|
||||
{ "sin", sin },
|
||||
{ "sqrt", sqrt },
|
||||
{ 0, 0 },
|
||||
};
|
||||
|
||||
// The symbol table: a chain of 'struct symrec'.
|
||||
static symrec *sym_table;
|
||||
|
||||
// Put functions in table.
|
||||
static void
|
||||
init_table (void)
|
||||
{
|
||||
for (int i = 0; funs[i].name; i++)
|
||||
{
|
||||
symrec *ptr = putsym (funs[i].name, TOK_FUN);
|
||||
ptr->value.fun = funs[i].fun;
|
||||
}
|
||||
}
|
||||
|
||||
symrec *
|
||||
putsym (char const *name, int sym_type)
|
||||
{
|
||||
symrec *res = (symrec *) malloc (sizeof (symrec));
|
||||
res->name = strdup (name);
|
||||
res->type = sym_type;
|
||||
res->value.var = 0; // Set value to 0 even if fun.
|
||||
res->next = sym_table;
|
||||
sym_table = res;
|
||||
return res;
|
||||
}
|
||||
|
||||
symrec *
|
||||
getsym (char const *name)
|
||||
{
|
||||
for (symrec *p = sym_table; p; p = p->next)
|
||||
if (strcmp (p->name, name) == 0)
|
||||
return p;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// How many symbols are registered.
|
||||
static int
|
||||
symbol_count (void)
|
||||
{
|
||||
int res = 0;
|
||||
for (symrec *p = sym_table; p; p = p->next)
|
||||
++res;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*------------.
|
||||
| Locations. |
|
||||
`------------*/
|
||||
|
||||
// Print *LOC on OUT. Do it in a compact way, that avoids redundancy.
|
||||
|
||||
static void
|
||||
location_print (FILE *out, YYLTYPE const * const loc)
|
||||
{
|
||||
fprintf (out, "%d.%d", loc->first_line, loc->first_column);
|
||||
|
||||
int end_col = 0 != loc->last_column ? loc->last_column - 1 : 0;
|
||||
if (loc->first_line < loc->last_line)
|
||||
fprintf (out, "-%d.%d", loc->last_line, end_col);
|
||||
else if (loc->first_column < end_col)
|
||||
fprintf (out, "-%d", end_col);
|
||||
}
|
||||
|
||||
|
||||
/*----------.
|
||||
| Scanner. |
|
||||
`----------*/
|
||||
|
||||
yytoken_kind_t
|
||||
yylex (const char **line, YYSTYPE *yylval, YYLTYPE *yylloc,
|
||||
const user_context *uctx)
|
||||
{
|
||||
int c;
|
||||
|
||||
// Ignore white space, get first nonwhite character.
|
||||
do {
|
||||
// Move the first position onto the last.
|
||||
yylloc->first_line = yylloc->last_line;
|
||||
yylloc->first_column = yylloc->last_column;
|
||||
|
||||
yylloc->last_column += 1;
|
||||
c = *((*line)++);
|
||||
} while (c == ' ' || c == '\t');
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case '+': return TOK_PLUS;
|
||||
case '-': return TOK_MINUS;
|
||||
case '*': return TOK_STAR;
|
||||
case '/': return TOK_SLASH;
|
||||
case '^': return TOK_CARET;
|
||||
case '=': return TOK_EQUAL;
|
||||
case '(': return TOK_LPAREN;
|
||||
case ')': return TOK_RPAREN;
|
||||
|
||||
case '!': return TOK_YYUNDEF;
|
||||
|
||||
case '\0': return TOK_YYEOF;
|
||||
|
||||
// Numbers.
|
||||
case '.':
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
{
|
||||
int nchars = 0;
|
||||
if (sscanf (*line - 1, "%lf%n", &yylval->TOK_NUM, &nchars) != 1)
|
||||
abort ();
|
||||
*line += nchars - 1;
|
||||
yylloc->last_column += nchars - 1;
|
||||
return TOK_NUM;
|
||||
}
|
||||
|
||||
// Identifiers.
|
||||
case 'a': case 'b': case 'c': case 'd': case 'e':
|
||||
case 'f': case 'g': case 'h': case 'i': case 'j':
|
||||
case 'k': case 'l': case 'm': case 'n': case 'o':
|
||||
case 'p': case 'q': case 'r': case 's': case 't':
|
||||
case 'u': case 'v': case 'w': case 'x': case 'y':
|
||||
case 'z':
|
||||
{
|
||||
int nchars = 0;
|
||||
char buf[100];
|
||||
if (sscanf (*line - 1, "%99[a-z]%n", buf, &nchars) != 1)
|
||||
abort ();
|
||||
*line += nchars - 1;
|
||||
yylloc->last_column += nchars - 1;
|
||||
if (strcmp (buf, "exit") == 0)
|
||||
return TOK_EXIT;
|
||||
else
|
||||
{
|
||||
symrec *s = getsym (buf);
|
||||
if (!s)
|
||||
s = putsym (buf, TOK_VAR);
|
||||
yylval->TOK_VAR = s;
|
||||
return s->type;
|
||||
}
|
||||
}
|
||||
|
||||
// Stray characters.
|
||||
default:
|
||||
yyerror (yylloc, uctx, _("syntax error: invalid character: %c"), c);
|
||||
return TOK_YYerror;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*---------.
|
||||
| Parser. |
|
||||
`---------*/
|
||||
|
||||
|
||||
static const char *
|
||||
error_format_string (int argc)
|
||||
{
|
||||
switch (argc)
|
||||
{
|
||||
default: // Avoid compiler warnings.
|
||||
case 0: return _("%@: syntax error");
|
||||
case 1: return _("%@: syntax error: unexpected %u");
|
||||
// TRANSLATORS: '%@' is a location in a file, '%u' is an
|
||||
// "unexpected token", and '%0e', '%1e'... are expected tokens
|
||||
// at this point.
|
||||
//
|
||||
// For instance on the expression "1 + * 2", you'd get
|
||||
//
|
||||
// 1.5: syntax error: expected - or ( or number or function or variable before *
|
||||
case 2: return _("%@: syntax error: expected %0e before %u");
|
||||
case 3: return _("%@: syntax error: expected %0e or %1e before %u");
|
||||
case 4: return _("%@: syntax error: expected %0e or %1e or %2e before %u");
|
||||
case 5: return _("%@: syntax error: expected %0e or %1e or %2e or %3e before %u");
|
||||
case 6: return _("%@: syntax error: expected %0e or %1e or %2e or %3e or %4e before %u");
|
||||
case 7: return _("%@: syntax error: expected %0e or %1e or %2e or %3e or %4e or %5e before %u");
|
||||
case 8: return _("%@: syntax error: expected %0e or %1e or %2e or %3e or %4e or %5e etc., before %u");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
yyreport_syntax_error (const yypcontext_t *ctx, const user_context *uctx)
|
||||
{
|
||||
if (uctx->silent)
|
||||
return 0;
|
||||
|
||||
enum { ARGS_MAX = 6 };
|
||||
yysymbol_kind_t arg[ARGS_MAX];
|
||||
int argsize = yypcontext_expected_tokens (ctx, arg, ARGS_MAX);
|
||||
if (argsize < 0)
|
||||
return argsize;
|
||||
const int too_many_expected_tokens = argsize == 0 && arg[0] != YYSYMBOL_YYEMPTY;
|
||||
if (too_many_expected_tokens)
|
||||
argsize = ARGS_MAX;
|
||||
const char *format = error_format_string (1 + argsize + too_many_expected_tokens);
|
||||
|
||||
const YYLTYPE *loc = yypcontext_location (ctx);
|
||||
while (*format)
|
||||
// %@: location.
|
||||
if (format[0] == '%' && format[1] == '@')
|
||||
{
|
||||
YYLOCATION_PRINT (stderr, loc);
|
||||
format += 2;
|
||||
}
|
||||
// %u: unexpected token.
|
||||
else if (format[0] == '%' && format[1] == 'u')
|
||||
{
|
||||
fputs (yysymbol_name (yypcontext_token (ctx)), stderr);
|
||||
format += 2;
|
||||
}
|
||||
// %0e, %1e...: expected token.
|
||||
else if (format[0] == '%'
|
||||
&& isdigit ((unsigned char) format[1])
|
||||
&& format[2] == 'e'
|
||||
&& (format[1] - '0') < argsize)
|
||||
{
|
||||
int i = format[1] - '0';
|
||||
fputs (yysymbol_name (arg[i]), stderr);
|
||||
format += 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
fputc (*format, stderr);
|
||||
++format;
|
||||
}
|
||||
fputc ('\n', stderr);
|
||||
|
||||
// Quote the source line.
|
||||
{
|
||||
fprintf (stderr, "%5d | %s\n", loc->first_line, uctx->line);
|
||||
fprintf (stderr, "%5s | %*s", "", loc->first_column, "^");
|
||||
for (int i = loc->last_column - loc->first_column - 1; 0 < i; --i)
|
||||
putc ('~', stderr);
|
||||
putc ('\n', stderr);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Called by yyparse on errors to report the error to the user.
|
||||
void
|
||||
yyerror (const YYLTYPE *loc, const user_context *uctx, char const *format, ...)
|
||||
{
|
||||
if (uctx->silent)
|
||||
return;
|
||||
|
||||
YYLOCATION_PRINT (stderr, loc);
|
||||
fputs (": ", stderr);
|
||||
va_list args;
|
||||
va_start (args, format);
|
||||
vfprintf (stderr, format, args);
|
||||
va_end (args);
|
||||
putc ('\n', stderr);
|
||||
}
|
||||
|
||||
|
||||
// Return a newly allocated copy of at most N bytes of STRING. In
|
||||
// other words, return a copy of the initial segment of length N of
|
||||
// STRING.
|
||||
static char *
|
||||
xstrndup (const char *string, size_t n)
|
||||
{
|
||||
// len = strnlen (string, n), portably.
|
||||
const char *end = memchr (string, '\0', n);
|
||||
size_t len = end ? (size_t) (end - string) : n;
|
||||
char *new = malloc (len + 1);
|
||||
if (!new)
|
||||
abort ();
|
||||
new[len] = '\0';
|
||||
return memcpy (new, string, len);
|
||||
}
|
||||
|
||||
|
||||
/*-----------.
|
||||
| Readline. |
|
||||
`-----------*/
|
||||
|
||||
// Parse (and execute) this line.
|
||||
static int
|
||||
process_line (YYLTYPE *lloc, const char *line)
|
||||
{
|
||||
user_context uctx = {0, line};
|
||||
yypstate *ps = yypstate_new ();
|
||||
int status = 0;
|
||||
do {
|
||||
YYSTYPE lval;
|
||||
yytoken_kind_t token = yylex (&line, &lval, lloc, &uctx);
|
||||
status = yypush_parse (ps, token, &lval, lloc, &uctx);
|
||||
} while (status == YYPUSH_MORE);
|
||||
yypstate_delete (ps);
|
||||
lloc->last_line++;
|
||||
lloc->last_column = 1;
|
||||
return status;
|
||||
}
|
||||
|
||||
// Get the list of possible tokens after INPUT was read.
|
||||
// Returns a nonnegative.
|
||||
static int
|
||||
expected_tokens (const char *input,
|
||||
int *tokens, int ntokens)
|
||||
{
|
||||
YYDPRINTF ((stderr, "expected_tokens (\"%s\")", input));
|
||||
user_context uctx = {1, input};
|
||||
|
||||
// Parse the current state of the line.
|
||||
yypstate *ps = yypstate_new ();
|
||||
int status = 0;
|
||||
YYLTYPE lloc = { 1, 1, 1, 1 };
|
||||
do {
|
||||
YYSTYPE lval;
|
||||
yytoken_kind_t token = yylex (&input, &lval, &lloc, &uctx);
|
||||
// Don't let the parse know when we reach the end of input.
|
||||
if (token == TOK_YYEOF)
|
||||
break;
|
||||
status = yypush_parse (ps, token, &lval, &lloc, &uctx);
|
||||
} while (status == YYPUSH_MORE);
|
||||
|
||||
int res = 0;
|
||||
// If there were parse errors, don't propose completions.
|
||||
if (!ps->yynerrs)
|
||||
{
|
||||
// Then query for the accepted tokens at this point.
|
||||
res = yypstate_expected_tokens (ps, tokens, ntokens);
|
||||
if (res < 0)
|
||||
abort ();
|
||||
}
|
||||
yypstate_delete (ps);
|
||||
return res;
|
||||
}
|
||||
|
||||
// Attempt to complete on the contents of TEXT. START and END bound
|
||||
// the region of rl_line_buffer that contains the word to complete.
|
||||
// TEXT is the word to complete. We can use the entire contents of
|
||||
// rl_line_buffer in case we want to do some simple parsing. Return
|
||||
// the array of matches, or NULL if there aren't any.
|
||||
static char **
|
||||
completion (const char *text, int start, int end)
|
||||
{
|
||||
YYDPRINTF ((stderr, "completion (\"%.*s[%.*s]%s\")\n",
|
||||
start, rl_line_buffer,
|
||||
end - start, rl_line_buffer + start,
|
||||
rl_line_buffer + end));
|
||||
|
||||
// Get list of token numbers.
|
||||
int tokens[YYNTOKENS];
|
||||
char *line = xstrndup (rl_line_buffer, (size_t) start);
|
||||
int ntokens = expected_tokens (line, tokens, YYNTOKENS);
|
||||
free (line);
|
||||
|
||||
// Build MATCHES, the list of possible completions.
|
||||
const size_t len = strlen (text);
|
||||
// Need initial prefix and final NULL.
|
||||
char **matches
|
||||
= calloc ((size_t) ntokens + (size_t) symbol_count () + 2, sizeof *matches);
|
||||
if (!matches)
|
||||
abort ();
|
||||
int match = 1;
|
||||
for (int i = 0; i < ntokens; ++i)
|
||||
switch (tokens[i])
|
||||
{
|
||||
case YYSYMBOL_FUN:
|
||||
for (symrec *s = sym_table; s; s = s->next)
|
||||
if (s->type == TOK_FUN && strncmp (text, s->name, len) == 0)
|
||||
matches[match++] = strdup (s->name);
|
||||
break;
|
||||
case YYSYMBOL_VAR:
|
||||
for (symrec *s = sym_table; s; s = s->next)
|
||||
if (s->type == TOK_VAR && strncmp (text, s->name, len) == 0)
|
||||
matches[match++] = strdup (s->name);
|
||||
break;
|
||||
default:
|
||||
{
|
||||
const char* token = yysymbol_name (tokens[i]);
|
||||
if (strncmp (text, token, len) == 0)
|
||||
matches[match++] = strdup (token);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Find the longest common prefix, and install it in matches[0], as
|
||||
// required by readline.
|
||||
if (match == 1)
|
||||
matches[0] = strdup (text);
|
||||
else
|
||||
{
|
||||
size_t lcplen = strlen (matches[1]);
|
||||
for (int i = 2; i < match && lcplen; ++i)
|
||||
for (size_t j = 0; j < lcplen; ++j)
|
||||
if (matches[1][j] != matches[i][j])
|
||||
lcplen = j;
|
||||
matches[0] = xstrndup (matches[1], lcplen);
|
||||
}
|
||||
|
||||
if (yydebug)
|
||||
{
|
||||
fprintf (stderr, "completion (\"%.*s[%.*s]%s\") = ",
|
||||
start, rl_line_buffer,
|
||||
end - start, rl_line_buffer + start,
|
||||
rl_line_buffer + end);
|
||||
for (int i = 1; matches[i]; ++i)
|
||||
fprintf (stderr, "%s%s",
|
||||
i == 1 ? "{" : ", ",
|
||||
matches[i]);
|
||||
fprintf (stderr, "}\n");
|
||||
}
|
||||
|
||||
// Don't fall back to proposing file names.
|
||||
rl_attempted_completion_over = 1;
|
||||
return matches;
|
||||
}
|
||||
|
||||
static void
|
||||
init_readline (void)
|
||||
{
|
||||
// Allow conditional parsing of the ~/.inputrc file.
|
||||
rl_readline_name = "bistromathic";
|
||||
|
||||
// Tell the completer that we want a crack first.
|
||||
rl_attempted_completion_function = completion;
|
||||
|
||||
// The basic list of characters that signal a break between words
|
||||
// for the completer routine.
|
||||
rl_basic_word_break_characters = " \t\n\"\\'`@$><=;|&{(+-*/^)";
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*-------.
|
||||
| Main. |
|
||||
`-------*/
|
||||
|
||||
int
|
||||
main (int argc, char const* argv[])
|
||||
{
|
||||
#if defined ENABLE_NLS && ENABLE_NLS
|
||||
// Set up internationalization.
|
||||
setlocale (LC_ALL, "");
|
||||
// Use Bison's standard translation catalog for error messages
|
||||
// (the generated messages).
|
||||
bindtextdomain ("bison-runtime", BISON_LOCALEDIR);
|
||||
// The translation catalog of bistromathic is actually included in
|
||||
// Bison's. In your own project, use the name of your project.
|
||||
bindtextdomain ("bison", LOCALEDIR);
|
||||
textdomain ("bison");
|
||||
#endif
|
||||
|
||||
// Enable parse traces on option -p.
|
||||
if (1 < argc && strcmp (argv[1], "-p") == 0)
|
||||
yydebug = 1;
|
||||
init_table ();
|
||||
init_readline ();
|
||||
YYLTYPE lloc = {1, 1, 1, 1};
|
||||
while (!done)
|
||||
{
|
||||
char *line = readline ("> ");
|
||||
if (!line)
|
||||
{
|
||||
// Finish the line started by the prompt.
|
||||
putchar ('\n');
|
||||
break;
|
||||
}
|
||||
if (*line)
|
||||
add_history (line);
|
||||
process_line (&lloc, line);
|
||||
free (line);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
# This Makefile is designed to be simple and readable. It does not
|
||||
# aim at portability. It requires GNU Make.
|
||||
|
||||
BASE = calc
|
||||
BISON = bison
|
||||
|
||||
all: $(BASE)
|
||||
|
||||
%.c %.h %.html %.xml %.gv: %.y
|
||||
$(BISON) $(BISONFLAGS) --header --html --graph -o $*.c $<
|
||||
|
||||
$(BASE): $(BASE).o
|
||||
$(CC) $(CFLAGS) -o $@ $^
|
||||
|
||||
run: $(BASE)
|
||||
@echo "Type arithmetic expressions. Quit with ctrl-d."
|
||||
./$<
|
||||
|
||||
CLEANFILES = \
|
||||
$(BASE) *.o $(BASE).[ch] $(BASE).output $(BASE).xml $(BASE).html $(BASE).gv
|
||||
|
||||
clean:
|
||||
rm -f $(CLEANFILES)
|
||||
@@ -0,0 +1,23 @@
|
||||
# calc - calculator with Bison
|
||||
|
||||
This directory contains calc, the traditional example of using Bison to
|
||||
build a simple calculator.
|
||||
|
||||
<!---
|
||||
Local Variables:
|
||||
fill-column: 76
|
||||
ispell-dictionary: "american"
|
||||
End:
|
||||
|
||||
Copyright (C) 2019-2021 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of Bison, the GNU Compiler Compiler.
|
||||
|
||||
Permission is granted to copy, distribute and/or modify this document
|
||||
under the terms of the GNU Free Documentation License, Version 1.3 or
|
||||
any later version published by the Free Software Foundation; with no
|
||||
Invariant Sections, with no Front-Cover Texts, and with no Back-Cover
|
||||
Texts. A copy of the license is included in the "GNU Free
|
||||
Documentation License" file as part of this distribution.
|
||||
|
||||
--->
|
||||
@@ -0,0 +1,43 @@
|
||||
#! /bin/sh
|
||||
|
||||
# Copyright (C) 2019-2021 Free Software Foundation, Inc.
|
||||
#
|
||||
# This program 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.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
cat >input <<EOF
|
||||
1+2*3
|
||||
EOF
|
||||
run 0 7
|
||||
|
||||
cat >input <<EOF
|
||||
1 - 2 - 3
|
||||
EOF
|
||||
run 0 -4
|
||||
|
||||
cat >input <<EOF
|
||||
8 / 2 / 2
|
||||
EOF
|
||||
run 0 2
|
||||
|
||||
cat >input <<EOF
|
||||
(1+2) * 3
|
||||
EOF
|
||||
run 0 9
|
||||
# Check the traces.
|
||||
run -noerr 0 9 -p
|
||||
|
||||
cat >input <<EOF
|
||||
1++2
|
||||
EOF
|
||||
run 0 "err: syntax error, unexpected '+', expecting number or '('"
|
||||
@@ -0,0 +1,102 @@
|
||||
%code top {
|
||||
#include <assert.h>
|
||||
#include <ctype.h> /* isdigit. */
|
||||
#include <stdio.h> /* printf. */
|
||||
#include <stdlib.h> /* abort. */
|
||||
#include <string.h> /* strcmp. */
|
||||
|
||||
int yylex (void);
|
||||
void yyerror (char const *);
|
||||
}
|
||||
|
||||
%define api.header.include {"calc.h"}
|
||||
|
||||
/* Generate YYSTYPE from the types used in %token and %type. */
|
||||
%define api.value.type union
|
||||
%token <double> NUM "number"
|
||||
%type <double> expr term fact
|
||||
|
||||
/* Generate the parser description file (calc.output). */
|
||||
%verbose
|
||||
|
||||
/* Nice error messages with details. */
|
||||
%define parse.error detailed
|
||||
|
||||
/* Enable run-time traces (yydebug). */
|
||||
%define parse.trace
|
||||
|
||||
/* Formatting semantic values in debug traces. */
|
||||
%printer { fprintf (yyo, "%g", $$); } <double>;
|
||||
|
||||
%% /* The grammar follows. */
|
||||
input:
|
||||
%empty
|
||||
| input line
|
||||
;
|
||||
|
||||
line:
|
||||
'\n'
|
||||
| expr '\n' { printf ("%.10g\n", $1); }
|
||||
| error '\n' { yyerrok; }
|
||||
;
|
||||
|
||||
expr:
|
||||
expr '+' term { $$ = $1 + $3; }
|
||||
| expr '-' term { $$ = $1 - $3; }
|
||||
| term
|
||||
;
|
||||
|
||||
term:
|
||||
term '*' fact { $$ = $1 * $3; }
|
||||
| term '/' fact { $$ = $1 / $3; }
|
||||
| fact
|
||||
;
|
||||
|
||||
fact:
|
||||
"number"
|
||||
| '(' expr ')' { $$ = $2; }
|
||||
;
|
||||
|
||||
%%
|
||||
|
||||
int
|
||||
yylex (void)
|
||||
{
|
||||
int c;
|
||||
|
||||
/* Ignore white space, get first nonwhite character. */
|
||||
while ((c = getchar ()) == ' ' || c == '\t')
|
||||
continue;
|
||||
|
||||
if (c == EOF)
|
||||
return 0;
|
||||
|
||||
/* Char starts a number => parse the number. */
|
||||
if (c == '.' || isdigit (c))
|
||||
{
|
||||
ungetc (c, stdin);
|
||||
if (scanf ("%lf", &yylval.NUM) != 1)
|
||||
abort ();
|
||||
return NUM;
|
||||
}
|
||||
|
||||
/* Any other character is a token by itself. */
|
||||
return c;
|
||||
}
|
||||
|
||||
/* Called by yyparse on error. */
|
||||
void
|
||||
yyerror (char const *s)
|
||||
{
|
||||
fprintf (stderr, "%s\n", s);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char const* argv[])
|
||||
{
|
||||
/* Enable parse traces on option -p. */
|
||||
for (int i = 1; i < argc; ++i)
|
||||
if (!strcmp (argv[i], "-p"))
|
||||
yydebug = 1;
|
||||
return yyparse ();
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
## Copyright (C) 2019-2021 Free Software Foundation, Inc.
|
||||
##
|
||||
## This program 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.
|
||||
##
|
||||
## This program is distributed in the hope that it will be useful,
|
||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU General Public License for more details.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License
|
||||
## along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
calcdir = $(docdir)/%D%
|
||||
|
||||
## ------ ##
|
||||
## Calc. ##
|
||||
## ------ ##
|
||||
|
||||
check_PROGRAMS += %D%/calc
|
||||
TESTS += %D%/calc.test
|
||||
EXTRA_DIST += %D%/calc.test
|
||||
nodist_%C%_calc_SOURCES = %D%/calc.y
|
||||
%D%/calc.c: $(dependencies)
|
||||
|
||||
# Don't use gnulib's system headers.
|
||||
%C%_calc_CPPFLAGS = -I$(top_srcdir)/%D% -I$(top_builddir)/%D%
|
||||
%C%_calc_CFLAGS = $(TEST_CFLAGS)
|
||||
|
||||
dist_calc_DATA = %D%/calc.y %D%/Makefile %D%/README.md
|
||||
CLEANFILES += %D%/calc.[ch] %D%/calc.output %D%/scan.c
|
||||
CLEANDIRS += %D%/*.dSYM
|
||||
@@ -0,0 +1,23 @@
|
||||
# This Makefile is designed to be simple and readable. It does not
|
||||
# aim at portability. It requires GNU Make.
|
||||
|
||||
BASE = c++-types
|
||||
BISON = bison
|
||||
|
||||
all: $(BASE)
|
||||
|
||||
%.c %.h %.xml %.gv: %.y
|
||||
$(BISON) $(BISONFLAGS) --header --graph -o $*.c $<
|
||||
|
||||
$(BASE): $(BASE).o
|
||||
$(CC) $(CFLAGS) -o $@ $^
|
||||
|
||||
run: $(BASE)
|
||||
@echo "Type C++ declarations or expressions. Quit with ctrl-d."
|
||||
./$<
|
||||
|
||||
CLEANFILES = \
|
||||
$(BASE) *.o $(BASE).[ch] $(BASE).output $(BASE).xml $(BASE).html $(BASE).gv
|
||||
|
||||
clean:
|
||||
rm -f $(CLEANFILES)
|
||||
@@ -0,0 +1,24 @@
|
||||
# glr
|
||||
|
||||
This example demonstrates the use of GLR parsers to handle (local)
|
||||
ambiguities in the C++ language. See the node "Merging GLR Parses" in
|
||||
Bison's documentation.
|
||||
|
||||
<!---
|
||||
Local Variables:
|
||||
fill-column: 76
|
||||
ispell-dictionary: "american"
|
||||
End:
|
||||
|
||||
Copyright (C) 2020-2021 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of Bison, the GNU Compiler Compiler.
|
||||
|
||||
Permission is granted to copy, distribute and/or modify this document
|
||||
under the terms of the GNU Free Documentation License, Version 1.3 or
|
||||
any later version published by the Free Software Foundation; with no
|
||||
Invariant Sections, with no Front-Cover Texts, and with no Back-Cover
|
||||
Texts. A copy of the license is included in the "GNU Free
|
||||
Documentation License" file as part of this distribution.
|
||||
|
||||
--->
|
||||
@@ -0,0 +1,47 @@
|
||||
#! /bin/sh
|
||||
|
||||
# Copyright (C) 2020-2021 Free Software Foundation, Inc.
|
||||
#
|
||||
# This program 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.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
cat >input <<EOF
|
||||
z + q;
|
||||
|
||||
T x;
|
||||
|
||||
T x = y;
|
||||
|
||||
x = y;
|
||||
|
||||
T (x) + y;
|
||||
|
||||
T (x);
|
||||
|
||||
T (y) = z + q;
|
||||
|
||||
T (y y) = z + q;
|
||||
|
||||
z + q;
|
||||
EOF
|
||||
run 0 "\
|
||||
1.0-5: +(z, q)
|
||||
3.0-3: <declare>(T, x)
|
||||
5.0-7: <init-declare>(T, x, y)
|
||||
7.0-5: =(x, y)
|
||||
9.0-9: +(<cast>(x, T), y)
|
||||
11.0-5: <OR>(<declare>(T, x), <cast>(x, T))
|
||||
13.0-13: <OR>(<init-declare>(T, y, +(z, q)), =(<cast>(y, T), +(z, q)))
|
||||
15.0-15: <error>
|
||||
17.0-5: +(z, q)
|
||||
err: 15.5: syntax error, unexpected identifier, expecting '=' or '+' or ')'"
|
||||
@@ -0,0 +1,340 @@
|
||||
/* -*- C -*-
|
||||
Copyright (C) 2020-2021 Free Software Foundation, Inc.
|
||||
|
||||
This program 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.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Simplified C++ Type and Expression Grammar.
|
||||
Written by Paul Hilfinger for Bison's test suite. */
|
||||
|
||||
%define api.pure
|
||||
%header
|
||||
%define api.header.include {"c++-types.h"}
|
||||
%locations
|
||||
%debug
|
||||
|
||||
/* Nice error messages with details. */
|
||||
%define parse.error detailed
|
||||
|
||||
%code requires
|
||||
{
|
||||
union Node {
|
||||
struct {
|
||||
int isNterm;
|
||||
int parents;
|
||||
} nodeInfo;
|
||||
struct {
|
||||
int isNterm; /* 1 */
|
||||
int parents;
|
||||
char const *form;
|
||||
union Node *children[3];
|
||||
} nterm;
|
||||
struct {
|
||||
int isNterm; /* 0 */
|
||||
int parents;
|
||||
char *text;
|
||||
} term;
|
||||
};
|
||||
typedef union Node Node;
|
||||
}
|
||||
|
||||
%define api.value.type union
|
||||
|
||||
%code
|
||||
{
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static Node *new_nterm (char const *, Node *, Node *, Node *);
|
||||
static Node *new_term (char *);
|
||||
static void free_node (Node *);
|
||||
static char *node_to_string (const Node *);
|
||||
static void node_print (FILE *, const Node *);
|
||||
static Node *stmt_merge (YYSTYPE x0, YYSTYPE x1);
|
||||
|
||||
static void yyerror (YYLTYPE const * const loc, const char *msg);
|
||||
static yytoken_kind_t yylex (YYSTYPE *lval, YYLTYPE *lloc);
|
||||
}
|
||||
|
||||
%expect-rr 1
|
||||
|
||||
%token
|
||||
TYPENAME "typename"
|
||||
ID "identifier"
|
||||
|
||||
%right '='
|
||||
%left '+'
|
||||
|
||||
%glr-parser
|
||||
|
||||
%type <Node *> stmt expr decl declarator TYPENAME ID
|
||||
%destructor { free_node ($$); } <Node *>
|
||||
%printer { node_print (yyo, $$); } <Node *>
|
||||
|
||||
%%
|
||||
|
||||
prog : %empty
|
||||
| prog stmt {
|
||||
YYLOCATION_PRINT (stdout, &@2);
|
||||
fputs (": ", stdout);
|
||||
node_print (stdout, $2);
|
||||
putc ('\n', stdout);
|
||||
fflush (stdout);
|
||||
free_node ($2);
|
||||
}
|
||||
;
|
||||
|
||||
stmt : expr ';' %merge <stmt_merge> { $$ = $1; }
|
||||
| decl %merge <stmt_merge>
|
||||
| error ';' { $$ = new_nterm ("<error>", NULL, NULL, NULL); }
|
||||
;
|
||||
|
||||
expr : ID
|
||||
| TYPENAME '(' expr ')'
|
||||
{ $$ = new_nterm ("<cast>(%s, %s)", $3, $1, NULL); }
|
||||
| expr '+' expr { $$ = new_nterm ("+(%s, %s)", $1, $3, NULL); }
|
||||
| expr '=' expr { $$ = new_nterm ("=(%s, %s)", $1, $3, NULL); }
|
||||
;
|
||||
|
||||
decl : TYPENAME declarator ';'
|
||||
{ $$ = new_nterm ("<declare>(%s, %s)", $1, $2, NULL); }
|
||||
| TYPENAME declarator '=' expr ';'
|
||||
{ $$ = new_nterm ("<init-declare>(%s, %s, %s)", $1,
|
||||
$2, $4); }
|
||||
;
|
||||
|
||||
declarator
|
||||
: ID
|
||||
| '(' declarator ')' { $$ = $2; }
|
||||
;
|
||||
|
||||
%%
|
||||
|
||||
/* A C error reporting function. */
|
||||
static void
|
||||
yyerror (YYLTYPE const * const loc, const char *msg)
|
||||
{
|
||||
YYLOCATION_PRINT (stderr, loc);
|
||||
fprintf (stderr, ": %s\n", msg);
|
||||
}
|
||||
|
||||
/* The input file. */
|
||||
FILE * input = NULL;
|
||||
|
||||
yytoken_kind_t
|
||||
yylex (YYSTYPE *lval, YYLTYPE *lloc)
|
||||
{
|
||||
static int lineNum = 1;
|
||||
static int colNum = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
int c;
|
||||
assert (!feof (input));
|
||||
c = getc (input);
|
||||
switch (c)
|
||||
{
|
||||
case EOF:
|
||||
return 0;
|
||||
case '\t':
|
||||
colNum = (colNum + 7) & ~7;
|
||||
break;
|
||||
case ' ': case '\f':
|
||||
colNum += 1;
|
||||
break;
|
||||
case '\n':
|
||||
lineNum += 1;
|
||||
colNum = 0;
|
||||
break;
|
||||
default:
|
||||
{
|
||||
yytoken_kind_t tok;
|
||||
lloc->first_line = lloc->last_line = lineNum;
|
||||
lloc->first_column = colNum;
|
||||
if (isalpha (c))
|
||||
{
|
||||
char buffer[256];
|
||||
unsigned i = 0;
|
||||
|
||||
do
|
||||
{
|
||||
buffer[i++] = (char) c;
|
||||
colNum += 1;
|
||||
assert (i != sizeof buffer - 1);
|
||||
c = getc (input);
|
||||
}
|
||||
while (isalnum (c) || c == '_');
|
||||
|
||||
ungetc (c, input);
|
||||
buffer[i++] = 0;
|
||||
if (isupper ((unsigned char) buffer[0]))
|
||||
{
|
||||
tok = TYPENAME;
|
||||
lval->TYPENAME = new_term (strcpy (malloc (i), buffer));
|
||||
}
|
||||
else
|
||||
{
|
||||
tok = ID;
|
||||
lval->ID = new_term (strcpy (malloc (i), buffer));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
colNum += 1;
|
||||
tok = c;
|
||||
}
|
||||
lloc->last_column = colNum;
|
||||
return tok;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Node *
|
||||
new_nterm (char const *form, Node *child0, Node *child1, Node *child2)
|
||||
{
|
||||
Node *res = malloc (sizeof *res);
|
||||
res->nterm.isNterm = 1;
|
||||
res->nterm.parents = 0;
|
||||
res->nterm.form = form;
|
||||
res->nterm.children[0] = child0;
|
||||
if (child0)
|
||||
child0->nodeInfo.parents += 1;
|
||||
res->nterm.children[1] = child1;
|
||||
if (child1)
|
||||
child1->nodeInfo.parents += 1;
|
||||
res->nterm.children[2] = child2;
|
||||
if (child2)
|
||||
child2->nodeInfo.parents += 1;
|
||||
return res;
|
||||
}
|
||||
|
||||
static Node *
|
||||
new_term (char *text)
|
||||
{
|
||||
Node *res = malloc (sizeof *res);
|
||||
res->term.isNterm = 0;
|
||||
res->term.parents = 0;
|
||||
res->term.text = text;
|
||||
return res;
|
||||
}
|
||||
|
||||
static void
|
||||
free_node (Node *node)
|
||||
{
|
||||
if (!node)
|
||||
return;
|
||||
node->nodeInfo.parents -= 1;
|
||||
/* Free only if 0 (last parent) or -1 (no parents). */
|
||||
if (node->nodeInfo.parents > 0)
|
||||
return;
|
||||
if (node->nodeInfo.isNterm == 1)
|
||||
{
|
||||
free_node (node->nterm.children[0]);
|
||||
free_node (node->nterm.children[1]);
|
||||
free_node (node->nterm.children[2]);
|
||||
}
|
||||
else
|
||||
free (node->term.text);
|
||||
free (node);
|
||||
}
|
||||
|
||||
static char *
|
||||
node_to_string (const Node *node)
|
||||
{
|
||||
char *res;
|
||||
if (!node)
|
||||
{
|
||||
res = malloc (1);
|
||||
res[0] = 0;
|
||||
}
|
||||
else if (node->nodeInfo.isNterm == 1)
|
||||
{
|
||||
char *child0 = node_to_string (node->nterm.children[0]);
|
||||
char *child1 = node_to_string (node->nterm.children[1]);
|
||||
char *child2 = node_to_string (node->nterm.children[2]);
|
||||
res = malloc (strlen (node->nterm.form) + strlen (child0)
|
||||
+ strlen (child1) + strlen (child2) + 1);
|
||||
sprintf (res, node->nterm.form, child0, child1, child2);
|
||||
free (child2);
|
||||
free (child1);
|
||||
free (child0);
|
||||
}
|
||||
else
|
||||
res = strdup (node->term.text);
|
||||
return res;
|
||||
}
|
||||
|
||||
static void
|
||||
node_print (FILE *out, const Node *n)
|
||||
{
|
||||
char *str = node_to_string (n);
|
||||
fputs (str, out);
|
||||
free (str);
|
||||
}
|
||||
|
||||
|
||||
static Node *
|
||||
stmt_merge (YYSTYPE x0, YYSTYPE x1)
|
||||
{
|
||||
return new_nterm ("<OR>(%s, %s)", x0.stmt, x1.stmt, NULL);
|
||||
}
|
||||
|
||||
static int
|
||||
process (const char *file)
|
||||
{
|
||||
int is_stdin = !file || strcmp (file, "-") == 0;
|
||||
if (is_stdin)
|
||||
input = stdin;
|
||||
else
|
||||
input = fopen (file, "r");
|
||||
assert (input);
|
||||
int status = yyparse ();
|
||||
if (!is_stdin)
|
||||
fclose (input);
|
||||
return status;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char **argv)
|
||||
{
|
||||
if (getenv ("YYDEBUG"))
|
||||
yydebug = 1;
|
||||
|
||||
int ran = 0;
|
||||
for (int i = 1; i < argc; ++i)
|
||||
// Enable parse traces on option -p.
|
||||
if (strcmp (argv[i], "-p") == 0)
|
||||
yydebug = 1;
|
||||
else
|
||||
{
|
||||
int status = process (argv[i]);
|
||||
ran = 1;
|
||||
if (!status)
|
||||
return status;
|
||||
}
|
||||
|
||||
if (!ran)
|
||||
{
|
||||
int status = process (NULL);
|
||||
if (!status)
|
||||
return status;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
## Copyright (C) 2020-2021 Free Software Foundation, Inc.
|
||||
##
|
||||
## This program 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.
|
||||
##
|
||||
## This program is distributed in the hope that it will be useful,
|
||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU General Public License for more details.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License
|
||||
## along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
glrdir = $(docdir)/%D%
|
||||
|
||||
## ----------- ##
|
||||
## c++-types. ##
|
||||
## ----------- ##
|
||||
|
||||
check_PROGRAMS += %D%/c++-types
|
||||
TESTS += %D%/c++-types.test
|
||||
EXTRA_DIST += %D%/c++-types.test
|
||||
nodist_%C%_c___types_SOURCES = %D%/c++-types.y
|
||||
%D%/c++-types.c: $(dependencies)
|
||||
|
||||
# Don't use gnulib's system headers.
|
||||
%C%_c___types_CPPFLAGS = -I$(top_srcdir)/%D% -I$(top_builddir)/%D%
|
||||
%C%_c___types_CFLAGS = $(TEST_CFLAGS)
|
||||
|
||||
dist_glr_DATA = %D%/c++-types.y %D%/Makefile %D%/README.md
|
||||
CLEANFILES += %D%/c++-types.[ch] %D%/c++-types.output
|
||||
CLEANDIRS += %D%/*.dSYM
|
||||
@@ -0,0 +1,29 @@
|
||||
# This Makefile is designed to be simple and readable. It does not
|
||||
# aim at portability. It requires GNU Make.
|
||||
|
||||
BASE = lexcalc
|
||||
BISON = bison
|
||||
FLEX = flex
|
||||
|
||||
all: $(BASE)
|
||||
|
||||
%.c %.h %.html %.xml %.gv: %.y
|
||||
$(BISON) $(BISONFLAGS) --header --html --graph -o $*.c $<
|
||||
|
||||
%.c: %.l
|
||||
$(FLEX) $(FLEXFLAGS) -o$@ $<
|
||||
|
||||
scan.o: parse.h
|
||||
$(BASE): parse.o scan.o
|
||||
$(CC) $(CFLAGS) -o $@ $^
|
||||
|
||||
run: $(BASE)
|
||||
@echo "Type arithmetic expressions. Quit with ctrl-d."
|
||||
./$<
|
||||
|
||||
CLEANFILES = \
|
||||
$(BASE) *.o \
|
||||
parse.[ch] parse.output parse.xml parse.html parse.gv \
|
||||
scan.c
|
||||
clean:
|
||||
rm -f $(CLEANFILES)
|
||||
@@ -0,0 +1,24 @@
|
||||
# lexcalc - calculator with Flex and Bison
|
||||
|
||||
This directory contains lexcalc, the traditional example of using Flex and
|
||||
Bison to build a simple calculator.
|
||||
|
||||
It features detailed syntax errors with locations.
|
||||
|
||||
<!---
|
||||
Local Variables:
|
||||
fill-column: 76
|
||||
ispell-dictionary: "american"
|
||||
End:
|
||||
|
||||
Copyright (C) 2018-2021 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of Bison, the GNU Compiler Compiler.
|
||||
|
||||
Permission is granted to copy, distribute and/or modify this document
|
||||
under the terms of the GNU Free Documentation License, Version 1.3 or
|
||||
any later version published by the Free Software Foundation; with no
|
||||
Invariant Sections, with no Front-Cover Texts, and with no Back-Cover
|
||||
Texts. A copy of the license is included in the "GNU Free
|
||||
Documentation License" file as part of this distribution.
|
||||
--->
|
||||
@@ -0,0 +1,46 @@
|
||||
#! /bin/sh
|
||||
|
||||
# Copyright (C) 2018-2021 Free Software Foundation, Inc.
|
||||
#
|
||||
# This program 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.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
cat >input <<EOF
|
||||
1+2*3
|
||||
EOF
|
||||
run 0 7
|
||||
|
||||
cat >input <<EOF
|
||||
1
|
||||
2
|
||||
3
|
||||
EOF
|
||||
run 0 '1
|
||||
2
|
||||
3'
|
||||
|
||||
cat >input <<EOF
|
||||
(1+2) * 3
|
||||
EOF
|
||||
run 0 9
|
||||
run -noerr 0 9 -p
|
||||
|
||||
cat >input <<EOF
|
||||
(1+2) *
|
||||
EOF
|
||||
run 1 'err: 1.8-2.0: syntax error, unexpected end of line, expecting ( or number'
|
||||
|
||||
cat >input <<EOF
|
||||
1 / (2 - 2)
|
||||
EOF
|
||||
run 1 'err: 1.1-11: error: division by zero'
|
||||
@@ -0,0 +1,44 @@
|
||||
## Copyright (C) 2018-2021 Free Software Foundation, Inc.
|
||||
##
|
||||
## This program 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.
|
||||
##
|
||||
## This program is distributed in the hope that it will be useful,
|
||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU General Public License for more details.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License
|
||||
## along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
lexcalcdir = $(docdir)/%D%
|
||||
|
||||
## --------- ##
|
||||
## LexCalc. ##
|
||||
## --------- ##
|
||||
|
||||
if FLEX_WORKS
|
||||
check_PROGRAMS += %D%/lexcalc
|
||||
TESTS += %D%/lexcalc.test
|
||||
nodist_%C%_lexcalc_SOURCES = %D%/parse.y %D%/parse.h %D%/scan.l
|
||||
# Don't use gnulib's system headers.
|
||||
%C%_lexcalc_CPPFLAGS = -I$(top_srcdir)/%D% -I$(top_builddir)/%D%
|
||||
# Fighting warnings triggered by Flex is just too painful.
|
||||
# %C%_lexcalc_CFLAGS = $(TEST_CFLAGS)
|
||||
endif FLEX_WORKS
|
||||
|
||||
%D%/parse.c: $(dependencies)
|
||||
|
||||
# Tell Make scan.o depends on parse.h, except that Make sees only
|
||||
# parse.c, not parse.h. We can't use BUILT_SOURCES to this end, since
|
||||
# we use the built bison.
|
||||
%D%/lexcalc$(DASH)scan.o: %D%/parse.c
|
||||
# Likewise, but for Automake before 1.16.
|
||||
%D%/examples_c_lexcalc_lexcalc$(DASH)scan.o: %D%/parse.c
|
||||
|
||||
EXTRA_DIST += %D%/lexcalc.test
|
||||
dist_lexcalc_DATA = %D%/parse.y %D%/scan.l %D%/Makefile %D%/README.md
|
||||
CLEANFILES += %D%/parse.[ch] %D%/scan.c %D%/parse.output
|
||||
CLEANDIRS += %D%/*.dSYM
|
||||
@@ -0,0 +1,138 @@
|
||||
/* Parser for lexcalc. -*- C -*-
|
||||
|
||||
Copyright (C) 2018-2021 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of Bison, the GNU Compiler Compiler.
|
||||
|
||||
This program 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.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
// Prologue (directives).
|
||||
%expect 0
|
||||
|
||||
// Emitted in the header file, after the definition of YYSTYPE.
|
||||
%code provides
|
||||
{
|
||||
// Tell Flex the expected prototype of yylex.
|
||||
#define YY_DECL \
|
||||
yytoken_kind_t yylex (YYSTYPE* yylval, YYLTYPE *yylloc, int *nerrs)
|
||||
YY_DECL;
|
||||
|
||||
void yyerror (const YYLTYPE *loc, int *nerrs, const char *msg);
|
||||
}
|
||||
|
||||
// Emitted on top of the implementation file.
|
||||
%code top
|
||||
{
|
||||
#include <stdio.h> // printf.
|
||||
#include <stdlib.h> // getenv.
|
||||
#include <string.h> // strcmp.
|
||||
}
|
||||
|
||||
// Include the header in the implementation rather than duplicating it.
|
||||
%define api.header.include {"parse.h"}
|
||||
|
||||
// Don't share global variables between the scanner and the parser.
|
||||
%define api.pure full
|
||||
|
||||
// To avoid name clashes (e.g., with C's EOF) prefix token definitions
|
||||
// with TOK_ (e.g., TOK_EOF).
|
||||
%define api.token.prefix {TOK_}
|
||||
|
||||
// %token and %type use genuine types (e.g., "%token <int>"). Let
|
||||
// %bison define YYSTYPE as a union of all these types.
|
||||
%define api.value.type union
|
||||
|
||||
// Generate detailed error messages.
|
||||
%define parse.error detailed
|
||||
|
||||
// with locations.
|
||||
%locations
|
||||
|
||||
// Enable debug traces (see yydebug in main).
|
||||
%define parse.trace
|
||||
|
||||
// Error count, exchanged between main, yyparse and yylex.
|
||||
%param {int *nerrs}
|
||||
|
||||
%token
|
||||
PLUS "+"
|
||||
MINUS "-"
|
||||
STAR "*"
|
||||
SLASH "/"
|
||||
LPAREN "("
|
||||
RPAREN ")"
|
||||
EOL "end of line"
|
||||
;
|
||||
|
||||
%token <int> NUM "number"
|
||||
%type <int> exp
|
||||
%printer { fprintf (yyo, "%d", $$); } <int>
|
||||
|
||||
// Precedence (from lowest to highest) and associativity.
|
||||
%left "+" "-"
|
||||
%left "*" "/"
|
||||
|
||||
%%
|
||||
// Rules.
|
||||
input:
|
||||
%empty
|
||||
| input line
|
||||
;
|
||||
|
||||
line:
|
||||
exp EOL { printf ("%d\n", $exp); }
|
||||
| error EOL { yyerrok; }
|
||||
;
|
||||
|
||||
exp:
|
||||
exp "+" exp { $$ = $1 + $3; }
|
||||
| exp "-" exp { $$ = $1 - $3; }
|
||||
| exp "*" exp { $$ = $1 * $3; }
|
||||
| exp "/" exp
|
||||
{
|
||||
if ($3 == 0)
|
||||
{
|
||||
yyerror (&@$, nerrs, "error: division by zero");
|
||||
YYERROR;
|
||||
}
|
||||
else
|
||||
$$ = $1 / $3;
|
||||
}
|
||||
| "(" exp ")" { $$ = $2; }
|
||||
| NUM { $$ = $1; }
|
||||
;
|
||||
%%
|
||||
// Epilogue (C code).
|
||||
|
||||
void yyerror (const YYLTYPE *loc, int *nerrs, const char *msg)
|
||||
{
|
||||
YYLOCATION_PRINT (stderr, loc);
|
||||
fprintf (stderr, ": %s\n", msg);
|
||||
++*nerrs;
|
||||
}
|
||||
|
||||
int main (int argc, const char *argv[])
|
||||
{
|
||||
// Possibly enable parser runtime debugging.
|
||||
yydebug = !!getenv ("YYDEBUG");
|
||||
// Enable parse traces on option -p.
|
||||
for (int i = 1; i < argc; ++i)
|
||||
if (strcmp (argv[i], "-p") == 0)
|
||||
yydebug = 1;
|
||||
|
||||
int nerrs = 0;
|
||||
yyparse (&nerrs);
|
||||
// Exit on failure if there were errors.
|
||||
return !!nerrs;
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/* Scanner for lexcalc. -*- C -*-
|
||||
|
||||
Copyright (C) 2018-2021 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of Bison, the GNU Compiler Compiler.
|
||||
|
||||
This program 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.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Prologue (directives). */
|
||||
|
||||
/* Disable Flex features we don't need, to avoid warnings. */
|
||||
%option nodefault noinput nounput noyywrap
|
||||
|
||||
%{
|
||||
#include <errno.h> /* errno, ERANGE */
|
||||
#include <limits.h> /* INT_MIN */
|
||||
#include <stdlib.h> /* strtol */
|
||||
|
||||
#include "parse.h"
|
||||
|
||||
// Each time a rule is matched, advance the end cursor/position.
|
||||
#define YY_USER_ACTION \
|
||||
yylloc->last_column += (int) yyleng;
|
||||
|
||||
// Move the first position onto the last.
|
||||
#define LOCATION_STEP() \
|
||||
do { \
|
||||
yylloc->first_line = yylloc->last_line; \
|
||||
yylloc->first_column = yylloc->last_column; \
|
||||
} while (0)
|
||||
%}
|
||||
|
||||
%%
|
||||
%{
|
||||
// Each time yylex is called, move the head position to the end one.
|
||||
LOCATION_STEP ();
|
||||
%}
|
||||
/* Rules. */
|
||||
|
||||
"+" return TOK_PLUS;
|
||||
"-" return TOK_MINUS;
|
||||
"*" return TOK_STAR;
|
||||
"/" return TOK_SLASH;
|
||||
|
||||
"(" return TOK_LPAREN;
|
||||
")" return TOK_RPAREN;
|
||||
|
||||
/* Scan an integer. */
|
||||
[0-9]+ {
|
||||
errno = 0;
|
||||
long n = strtol (yytext, NULL, 10);
|
||||
if (! (INT_MIN <= n && n <= INT_MAX && errno != ERANGE))
|
||||
yyerror (yylloc, nerrs, "integer is out of range");
|
||||
yylval->TOK_NUM = (int) n;
|
||||
return TOK_NUM;
|
||||
}
|
||||
|
||||
"\n" yylloc->last_line++; yylloc->last_column = 1; return TOK_EOL;
|
||||
|
||||
/* Ignore white spaces. */
|
||||
[ \t]+ LOCATION_STEP (); continue;
|
||||
|
||||
. yyerror (yylloc, nerrs, "syntax error, invalid character"); continue;
|
||||
|
||||
<<EOF>> return TOK_YYEOF;
|
||||
%%
|
||||
/* Epilogue (C code). */
|
||||
@@ -0,0 +1,26 @@
|
||||
## Copyright (C) 2018-2021 Free Software Foundation, Inc.
|
||||
##
|
||||
## This program 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.
|
||||
##
|
||||
## This program is distributed in the hope that it will be useful,
|
||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU General Public License for more details.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License
|
||||
## along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
cdir = $(docdir)/%D%
|
||||
dist_c_DATA = %D%/README.md
|
||||
|
||||
include %D%/bistromathic/local.mk
|
||||
include %D%/calc/local.mk
|
||||
include %D%/glr/local.mk
|
||||
include %D%/lexcalc/local.mk
|
||||
include %D%/mfcalc/local.mk
|
||||
include %D%/pushcalc/local.mk
|
||||
include %D%/reccalc/local.mk
|
||||
include %D%/rpcalc/local.mk
|
||||
@@ -0,0 +1,20 @@
|
||||
# This Makefile is designed to be simple and readable. It does not
|
||||
# aim at portability. It requires GNU Make.
|
||||
|
||||
BASE = mfcalc
|
||||
BISON = bison
|
||||
|
||||
all: $(BASE)
|
||||
|
||||
%.c %.html %.gv: %.y
|
||||
$(BISON) $(BISONFLAGS) --html --graph -o $*.c $<
|
||||
|
||||
%: %.c
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
run: $(BASE)
|
||||
@echo "Type arithmetic expressions. Quit with ctrl-d."
|
||||
./$<
|
||||
|
||||
clean:
|
||||
rm -f $(BASE) $(BASE).c $(BASE).html $(BASE).xml $(BASE).gv $(BASE).output
|
||||
@@ -0,0 +1,43 @@
|
||||
/* Functions for mfcalc. -*- C -*-
|
||||
|
||||
Copyright (C) 1988-1993, 1995, 1998-2015, 2018-2021 Free Software
|
||||
Foundation, Inc.
|
||||
|
||||
This file is part of Bison, the GNU Compiler Compiler.
|
||||
|
||||
This program 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.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Function type. */
|
||||
typedef double (func_t) (double);
|
||||
|
||||
/* Data type for links in the chain of symbols. */
|
||||
struct symrec
|
||||
{
|
||||
char *name; /* name of symbol */
|
||||
int type; /* type of symbol: either VAR or FUN */
|
||||
union
|
||||
{
|
||||
double var; /* value of a VAR */
|
||||
func_t *fun; /* value of a FUN */
|
||||
} value;
|
||||
struct symrec *next; /* link field */
|
||||
};
|
||||
|
||||
typedef struct symrec symrec;
|
||||
|
||||
/* The symbol table: a chain of 'struct symrec'. */
|
||||
extern symrec *sym_table;
|
||||
|
||||
symrec *putsym (char const *name, int sym_type);
|
||||
symrec *getsym (char const *name);
|
||||
@@ -0,0 +1,45 @@
|
||||
## Copyright (C) 2005-2006, 2008-2015, 2018-2021 Free Software
|
||||
## Foundation, Inc.
|
||||
##
|
||||
## This program 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.
|
||||
##
|
||||
## This program is distributed in the hope that it will be useful,
|
||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU General Public License for more details.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License
|
||||
## along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
## -------------------- ##
|
||||
## Building & testing. ##
|
||||
## -------------------- ##
|
||||
|
||||
BUILT_SOURCES += $(mfcalc_sources)
|
||||
CLEANFILES += %D%/mfcalc.[ch] %D%/mfcalc.output
|
||||
CLEANDIRS += %D%/*.dSYM
|
||||
|
||||
mfcalc_extracted = %D%/calc.h %D%/mfcalc.y
|
||||
mfcalc_sources = $(mfcalc_extracted)
|
||||
extracted += $(mfcalc_extracted)
|
||||
|
||||
check_PROGRAMS += %D%/mfcalc
|
||||
nodist_%C%_mfcalc_SOURCES = $(mfcalc_sources)
|
||||
%D%/mfcalc.c: $(dependencies)
|
||||
# Don't use gnulib's system headers.
|
||||
%C%_mfcalc_CPPFLAGS = -I$(top_srcdir)/%D% -I$(top_builddir)/%D%
|
||||
%C%_mfcalc_CFLAGS = $(TEST_CFLAGS)
|
||||
%C%_mfcalc_LDADD = -lm
|
||||
|
||||
dist_TESTS += %D%/mfcalc.test
|
||||
|
||||
## ------------ ##
|
||||
## Installing. ##
|
||||
## ------------ ##
|
||||
|
||||
mfcalcdir = $(docdir)/%D%
|
||||
mfcalc_DATA = $(mfcalc_extracted)
|
||||
dist_mfcalc_DATA = %D%/Makefile
|
||||
@@ -0,0 +1,39 @@
|
||||
#! /bin/sh
|
||||
|
||||
# Copyright (C) 2005-2015, 2018-2021 Free Software Foundation, Inc.
|
||||
#
|
||||
# This program 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.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
cat >input <<EOF
|
||||
1+2*3
|
||||
EOF
|
||||
run 0 7
|
||||
|
||||
cat >input <<EOF
|
||||
(1+2) * 3
|
||||
EOF
|
||||
run 0 9
|
||||
run -noerr 0 9 -p
|
||||
|
||||
cat >input <<EOF
|
||||
a = 256
|
||||
sqrt (a)
|
||||
EOF
|
||||
run 0 '256
|
||||
16'
|
||||
|
||||
cat >input <<EOF
|
||||
16 == 12
|
||||
EOF
|
||||
run 0 'err: syntax error'
|
||||
@@ -0,0 +1,208 @@
|
||||
/* Parser for mfcalc. -*- C -*-
|
||||
|
||||
Copyright (C) 1988-1993, 1995, 1998-2015, 2018-2021 Free Software
|
||||
Foundation, Inc.
|
||||
|
||||
This file is part of Bison, the GNU Compiler Compiler.
|
||||
|
||||
This program 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.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
%{
|
||||
#include <stdio.h> /* For printf, etc. */
|
||||
#include <math.h> /* For pow, used in the grammar. */
|
||||
#include "calc.h" /* Contains definition of 'symrec'. */
|
||||
int yylex (void);
|
||||
void yyerror (char const *);
|
||||
%}
|
||||
|
||||
%define api.value.type union /* Generate YYSTYPE from these types: */
|
||||
%token <double> NUM /* Double precision number. */
|
||||
%token <symrec*> VAR FUN /* Symbol table pointer: variable/function. */
|
||||
%nterm <double> exp
|
||||
|
||||
%precedence '='
|
||||
%left '-' '+'
|
||||
%left '*' '/'
|
||||
%precedence NEG /* negation--unary minus */
|
||||
%right '^' /* exponentiation */
|
||||
/* Generate the parser description file. */
|
||||
%verbose
|
||||
/* Enable run-time traces (yydebug). */
|
||||
%define parse.trace
|
||||
|
||||
/* Formatting semantic values. */
|
||||
%printer { fprintf (yyo, "%s", $$->name); } VAR;
|
||||
%printer { fprintf (yyo, "%s()", $$->name); } FUN;
|
||||
%printer { fprintf (yyo, "%g", $$); } <double>;
|
||||
%% /* The grammar follows. */
|
||||
input:
|
||||
%empty
|
||||
| input line
|
||||
;
|
||||
|
||||
line:
|
||||
'\n'
|
||||
| exp '\n' { printf ("%.10g\n", $1); }
|
||||
| error '\n' { yyerrok; }
|
||||
;
|
||||
|
||||
exp:
|
||||
NUM
|
||||
| VAR { $$ = $1->value.var; }
|
||||
| VAR '=' exp { $$ = $3; $1->value.var = $3; }
|
||||
| FUN '(' exp ')' { $$ = $1->value.fun ($3); }
|
||||
| exp '+' exp { $$ = $1 + $3; }
|
||||
| exp '-' exp { $$ = $1 - $3; }
|
||||
| exp '*' exp { $$ = $1 * $3; }
|
||||
| exp '/' exp { $$ = $1 / $3; }
|
||||
| '-' exp %prec NEG { $$ = -$2; }
|
||||
| exp '^' exp { $$ = pow ($1, $3); }
|
||||
| '(' exp ')' { $$ = $2; }
|
||||
;
|
||||
/* End of grammar. */
|
||||
%%
|
||||
|
||||
struct init
|
||||
{
|
||||
char const *name;
|
||||
func_t *fun;
|
||||
};
|
||||
|
||||
struct init const funs[] =
|
||||
{
|
||||
{ "atan", atan },
|
||||
{ "cos", cos },
|
||||
{ "exp", exp },
|
||||
{ "ln", log },
|
||||
{ "sin", sin },
|
||||
{ "sqrt", sqrt },
|
||||
{ 0, 0 },
|
||||
};
|
||||
|
||||
/* The symbol table: a chain of 'struct symrec'. */
|
||||
symrec *sym_table;
|
||||
|
||||
/* Put functions in table. */
|
||||
static void
|
||||
init_table (void)
|
||||
{
|
||||
for (int i = 0; funs[i].name; i++)
|
||||
{
|
||||
symrec *ptr = putsym (funs[i].name, FUN);
|
||||
ptr->value.fun = funs[i].fun;
|
||||
}
|
||||
}
|
||||
|
||||
/* The mfcalc code assumes that malloc and realloc
|
||||
always succeed, and that integer calculations
|
||||
never overflow. Production-quality code should
|
||||
not make these assumptions. */
|
||||
#include <assert.h>
|
||||
#include <stdlib.h> /* malloc, realloc. */
|
||||
#include <string.h> /* strlen. */
|
||||
|
||||
symrec *
|
||||
putsym (char const *name, int sym_type)
|
||||
{
|
||||
symrec *res = (symrec *) malloc (sizeof (symrec));
|
||||
res->name = strdup (name);
|
||||
res->type = sym_type;
|
||||
res->value.var = 0; /* Set value to 0 even if fun. */
|
||||
res->next = sym_table;
|
||||
sym_table = res;
|
||||
return res;
|
||||
}
|
||||
|
||||
symrec *
|
||||
getsym (char const *name)
|
||||
{
|
||||
for (symrec *p = sym_table; p; p = p->next)
|
||||
if (strcmp (p->name, name) == 0)
|
||||
return p;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stddef.h>
|
||||
|
||||
int
|
||||
yylex (void)
|
||||
{
|
||||
int c = getchar ();
|
||||
|
||||
/* Ignore white space, get first nonwhite character. */
|
||||
while (c == ' ' || c == '\t')
|
||||
c = getchar ();
|
||||
|
||||
if (c == EOF)
|
||||
return YYEOF;
|
||||
|
||||
/* Char starts a number => parse the number. */
|
||||
if (c == '.' || isdigit (c))
|
||||
{
|
||||
ungetc (c, stdin);
|
||||
if (scanf ("%lf", &yylval.NUM) != 1)
|
||||
abort ();
|
||||
return NUM;
|
||||
}
|
||||
|
||||
/* Char starts an identifier => read the name. */
|
||||
if (isalpha (c))
|
||||
{
|
||||
static ptrdiff_t bufsize = 0;
|
||||
static char *symbuf = 0;
|
||||
ptrdiff_t i = 0;
|
||||
do
|
||||
{
|
||||
/* If buffer is full, make it bigger. */
|
||||
if (bufsize <= i)
|
||||
{
|
||||
bufsize = 2 * bufsize + 40;
|
||||
symbuf = realloc (symbuf, (size_t) bufsize);
|
||||
}
|
||||
/* Add this character to the buffer. */
|
||||
symbuf[i++] = (char) c;
|
||||
/* Get another character. */
|
||||
c = getchar ();
|
||||
}
|
||||
while (isalnum (c));
|
||||
|
||||
ungetc (c, stdin);
|
||||
symbuf[i] = '\0';
|
||||
|
||||
symrec *s = getsym (symbuf);
|
||||
if (!s)
|
||||
s = putsym (symbuf, VAR);
|
||||
yylval.VAR = s; /* or yylval.FUN = s. */
|
||||
return s->type;
|
||||
}
|
||||
|
||||
/* Any other character is a token by itself. */
|
||||
return c;
|
||||
}
|
||||
|
||||
/* Called by yyparse on error. */
|
||||
void yyerror (char const *s)
|
||||
{
|
||||
fprintf (stderr, "%s\n", s);
|
||||
}
|
||||
|
||||
int main (int argc, char const* argv[])
|
||||
{
|
||||
/* Enable parse traces on option -p. */
|
||||
if (argc == 2 && strcmp(argv[1], "-p") == 0)
|
||||
yydebug = 1;
|
||||
init_table ();
|
||||
return yyparse ();
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
# This Makefile is designed to be simple and readable. It does not
|
||||
# aim at portability. It requires GNU Make.
|
||||
|
||||
BASE = calc
|
||||
BISON = bison
|
||||
|
||||
all: $(BASE)
|
||||
|
||||
%.c %.h %.html %.gv: %.y
|
||||
$(BISON) $(BISONFLAGS) --header --html --graph -o $*.c $<
|
||||
|
||||
$(BASE): $(BASE).o
|
||||
$(CC) $(CFLAGS) -o $@ $^
|
||||
|
||||
run: $(BASE)
|
||||
@echo "Type arithmetic expressions. Quit with ctrl-d."
|
||||
./$<
|
||||
|
||||
CLEANFILES = \
|
||||
$(BASE) *.o $(BASE).[ch] $(BASE).output $(BASE).xml $(BASE).html $(BASE).gv
|
||||
|
||||
clean:
|
||||
rm -f $(CLEANFILES)
|
||||
@@ -0,0 +1,32 @@
|
||||
# pushcalc - push parser with Bison
|
||||
|
||||
This directory contains pushcalc, the traditional calculator, implemented as
|
||||
a push parser.
|
||||
|
||||
Traditionally Bison is used to create so called "pull parsers": the user
|
||||
invokes the parser once, which repeatedly calls (pulls) the scanner until
|
||||
the input is drained.
|
||||
|
||||
This example demonstrates the "push parsers": the user calls scanner to
|
||||
fetch the next token, passes (pushes) it to the parser, and repeats the
|
||||
operation until the input is drained.
|
||||
|
||||
This example is a straightforward conversion of the 'calc' example to the
|
||||
push-parser model.
|
||||
|
||||
<!---
|
||||
Local Variables:
|
||||
fill-column: 76
|
||||
ispell-dictionary: "american"
|
||||
End:
|
||||
|
||||
Copyright (C) 2020-2021 Free Software Foundation, Inc.
|
||||
|
||||
Permission is granted to copy, distribute and/or modify this document
|
||||
under the terms of the GNU Free Documentation License, Version 1.3 or
|
||||
any later version published by the Free Software Foundation; with no
|
||||
Invariant Sections, with no Front-Cover Texts, and with no Back-Cover
|
||||
Texts. A copy of the license is included in the "GNU Free
|
||||
Documentation License" file as part of this distribution.
|
||||
|
||||
--->
|
||||
@@ -0,0 +1,42 @@
|
||||
#! /bin/sh
|
||||
|
||||
# Copyright (C) 2020-2021 Free Software Foundation, Inc.
|
||||
#
|
||||
# This program 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.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
cat >input <<EOF
|
||||
1+2*3
|
||||
EOF
|
||||
run 0 7
|
||||
|
||||
cat >input <<EOF
|
||||
1 - 2 - 3
|
||||
EOF
|
||||
run 0 -4
|
||||
|
||||
cat >input <<EOF
|
||||
8 / 2 / 2
|
||||
EOF
|
||||
run 0 2
|
||||
|
||||
cat >input <<EOF
|
||||
(1+2) * 3
|
||||
EOF
|
||||
run 0 9
|
||||
run -noerr 0 9 -p
|
||||
|
||||
cat >input <<EOF
|
||||
1++2
|
||||
EOF
|
||||
run 0 "err: syntax error, unexpected '+', expecting number or '('"
|
||||
@@ -0,0 +1,134 @@
|
||||
/* Parser and scanner for pushcalc. -*- C -*-
|
||||
|
||||
Copyright (C) 2020-2021 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of Bison, the GNU Compiler Compiler.
|
||||
|
||||
This program 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.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
%code top {
|
||||
#include <ctype.h> /* isdigit. */
|
||||
#include <stdio.h> /* printf. */
|
||||
#include <stdlib.h> /* abort. */
|
||||
#include <string.h> /* strcmp. */
|
||||
}
|
||||
|
||||
%code {
|
||||
int yylex (YYSTYPE *yylval);
|
||||
void yyerror (char const *);
|
||||
}
|
||||
|
||||
%define api.header.include {"calc.h"}
|
||||
|
||||
/* Generate YYSTYPE from the types used in %token and %type. */
|
||||
%define api.value.type union
|
||||
%token <double> NUM "number"
|
||||
%type <double> expr term fact
|
||||
|
||||
/* Don't share global variables between the scanner and the parser. */
|
||||
%define api.pure full
|
||||
/* Generate a push parser. */
|
||||
%define api.push-pull push
|
||||
|
||||
/* Nice error messages with details. */
|
||||
%define parse.error detailed
|
||||
|
||||
/* Generate the parser description file (calc.output). */
|
||||
%verbose
|
||||
|
||||
/* Enable run-time traces (yydebug). */
|
||||
%define parse.trace
|
||||
|
||||
/* Formatting semantic values in debug traces. */
|
||||
%printer { fprintf (yyo, "%g", $$); } <double>;
|
||||
|
||||
%% /* The grammar follows. */
|
||||
input:
|
||||
%empty
|
||||
| input line
|
||||
;
|
||||
|
||||
line:
|
||||
'\n'
|
||||
| expr '\n' { printf ("%.10g\n", $1); }
|
||||
| error '\n' { yyerrok; }
|
||||
;
|
||||
|
||||
expr:
|
||||
expr '+' term { $$ = $1 + $3; }
|
||||
| expr '-' term { $$ = $1 - $3; }
|
||||
| term
|
||||
;
|
||||
|
||||
term:
|
||||
term '*' fact { $$ = $1 * $3; }
|
||||
| term '/' fact { $$ = $1 / $3; }
|
||||
| fact
|
||||
;
|
||||
|
||||
fact:
|
||||
"number"
|
||||
| '(' expr ')' { $$ = $expr; }
|
||||
;
|
||||
|
||||
%%
|
||||
|
||||
int
|
||||
yylex (YYSTYPE *yylval)
|
||||
{
|
||||
int c;
|
||||
|
||||
/* Ignore white space, get first nonwhite character. */
|
||||
while ((c = getchar ()) == ' ' || c == '\t')
|
||||
continue;
|
||||
|
||||
if (c == EOF)
|
||||
return 0;
|
||||
|
||||
/* Char starts a number => parse the number. */
|
||||
if (c == '.' || isdigit (c))
|
||||
{
|
||||
ungetc (c, stdin);
|
||||
if (scanf ("%lf", &yylval->NUM) != 1)
|
||||
abort ();
|
||||
return NUM;
|
||||
}
|
||||
|
||||
/* Any other character is a token by itself. */
|
||||
return c;
|
||||
}
|
||||
|
||||
/* Called by yyparse on error. */
|
||||
void
|
||||
yyerror (char const *s)
|
||||
{
|
||||
fprintf (stderr, "%s\n", s);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char const* argv[])
|
||||
{
|
||||
/* Enable parse traces on option -p. */
|
||||
for (int i = 1; i < argc; ++i)
|
||||
if (!strcmp (argv[i], "-p"))
|
||||
yydebug = 1;
|
||||
int status;
|
||||
yypstate *ps = yypstate_new ();
|
||||
do {
|
||||
YYSTYPE lval;
|
||||
status = yypush_parse (ps, yylex (&lval), &lval);
|
||||
} while (status == YYPUSH_MORE);
|
||||
yypstate_delete (ps);
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
## Copyright (C) 2020-2021 Free Software Foundation, Inc.
|
||||
##
|
||||
## This program 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.
|
||||
##
|
||||
## This program is distributed in the hope that it will be useful,
|
||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU General Public License for more details.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License
|
||||
## along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
pushcalcdir = $(docdir)/%D%
|
||||
|
||||
## ------ ##
|
||||
## Calc. ##
|
||||
## ------ ##
|
||||
|
||||
check_PROGRAMS += %D%/calc
|
||||
TESTS += %D%/calc.test
|
||||
EXTRA_DIST += %D%/calc.test
|
||||
nodist_%C%_calc_SOURCES = %D%/calc.y
|
||||
%D%/calc.c: $(dependencies)
|
||||
|
||||
# Don't use gnulib's system headers.
|
||||
%C%_calc_CPPFLAGS = -I$(top_srcdir)/%D% -I$(top_builddir)/%D%
|
||||
%C%_calc_CFLAGS = $(TEST_CFLAGS)
|
||||
|
||||
dist_pushcalc_DATA = %D%/calc.y %D%/Makefile %D%/README.md
|
||||
CLEANFILES += %D%/calc.[ch] %D%/calc.output
|
||||
CLEANDIRS += %D%/*.dSYM
|
||||
@@ -0,0 +1,30 @@
|
||||
# This Makefile is designed to be simple and readable. It does not
|
||||
# aim at portability. It requires GNU Make.
|
||||
|
||||
BASE = reccalc
|
||||
BISON = bison
|
||||
FLEX = flex
|
||||
|
||||
all: $(BASE)
|
||||
|
||||
%.c %.h %.xml %.gv: %.y
|
||||
$(BISON) $(BISONFLAGS) --header --graph -o $*.c $<
|
||||
|
||||
%.c %.h: %.l
|
||||
$(FLEX) $(FLEXFLAGS) -o$*.c --header=$*.h $<
|
||||
|
||||
scan.o: parse.h
|
||||
parse.o: scan.h
|
||||
$(BASE): parse.o scan.o
|
||||
$(CC) $(CFLAGS) -o $@ $^
|
||||
|
||||
run: $(BASE)
|
||||
@echo "Type arithmetic expressions. Quit with ctrl-d."
|
||||
./$<
|
||||
|
||||
CLEANFILES = \
|
||||
$(BASE) *.o \
|
||||
parse.[ch] parse.output parse.xml parse.html parse.gv \
|
||||
scan.[ch]
|
||||
clean:
|
||||
rm -f $(CLEANFILES)
|
||||
@@ -0,0 +1,38 @@
|
||||
# reccalc - recursive calculator with Flex and Bison
|
||||
|
||||
In this example the generated parser is pure and reentrant: it can be used
|
||||
concurrently in different threads, or recursively. As a proof of this
|
||||
reentrancy, expressions in parenthesis are tokenized as strings, and then
|
||||
recursively parsed from the parser:
|
||||
|
||||
```
|
||||
exp: STR
|
||||
{
|
||||
result r = parse_string ($1);
|
||||
free ($1);
|
||||
if (r.nerrs)
|
||||
{
|
||||
res->nerrs += r.nerrs;
|
||||
YYERROR;
|
||||
}
|
||||
else
|
||||
$$ = r.value;
|
||||
}
|
||||
```
|
||||
|
||||
<!---
|
||||
Local Variables:
|
||||
fill-column: 76
|
||||
ispell-dictionary: "american"
|
||||
End:
|
||||
|
||||
Copyright (C) 2018-2021 Free Software Foundation, Inc.
|
||||
|
||||
Permission is granted to copy, distribute and/or modify this document
|
||||
under the terms of the GNU Free Documentation License, Version 1.3 or
|
||||
any later version published by the Free Software Foundation; with no
|
||||
Invariant Sections, with no Front-Cover Texts, and with no Back-Cover
|
||||
Texts. A copy of the license is included in the "GNU Free
|
||||
Documentation License" file as part of this distribution.
|
||||
|
||||
--->
|
||||
@@ -0,0 +1,83 @@
|
||||
## Copyright (C) 2019-2021 Free Software Foundation, Inc.
|
||||
##
|
||||
## This program 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.
|
||||
##
|
||||
## This program is distributed in the hope that it will be useful,
|
||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU General Public License for more details.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License
|
||||
## along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
reccalcdir = $(docdir)/%D%
|
||||
|
||||
## --------- ##
|
||||
## RecCalc. ##
|
||||
## --------- ##
|
||||
|
||||
if FLEX_WORKS
|
||||
check_PROGRAMS += %D%/reccalc
|
||||
TESTS += %D%/reccalc.test
|
||||
nodist_%C%_reccalc_SOURCES = %D%/parse.y %D%/scan.h %D%/scan.c
|
||||
BUILT_SOURCES += $(nodist_%C%_reccalc_SOURCES)
|
||||
# Don't use gnulib's system headers.
|
||||
%C%_reccalc_CPPFLAGS = -I$(top_srcdir)/%D% -I$(top_builddir)/%D%
|
||||
# Fighting warnings triggered by Flex is just too painful.
|
||||
# %C%_reccalc_CFLAGS = $(TEST_CFLAGS)
|
||||
endif FLEX_WORKS
|
||||
|
||||
%D%/parse.c: $(dependencies)
|
||||
|
||||
# Tell Make that parse.o depends on scan.h, so that scan.h is built
|
||||
# before parse.o. Obfuscate the name of the target, otherwise
|
||||
# Automake removes its recipe for parse.o and leaves only our
|
||||
# additional dependency.
|
||||
DASH = -
|
||||
%D%/reccalc$(DASH)parse.o: %D%/scan.h
|
||||
# Tell Make scan.o depends on parse.h, except that Make sees only
|
||||
# parse.c, not parse.h. We can't use BUILT_SOURCES to this end, since
|
||||
# we use the built bison.
|
||||
%D%/reccalc$(DASH)scan.o: %D%/parse.c
|
||||
|
||||
# Likewise, but for Automake before 1.16.
|
||||
%D%/examples_c_reccalc_reccalc$(DASH)parse.o: %D%/scan.h
|
||||
%D%/examples_c_reccalc_reccalc$(DASH)scan.o: %D%/parse.c
|
||||
|
||||
## See "info automake 'Multiple Outputs'" for this rule.
|
||||
%D%/scan.c %D%/scan.h: %D%/scan.stamp
|
||||
## Recover from the removal of $@
|
||||
@if test -f $@; then :; else \
|
||||
trap 'rm -rf %D%/scan.lock %D%/scan.stamp' 1 2 13 15; \
|
||||
## mkdir is a portable test-and-set
|
||||
if mkdir %D%/scan.lock 2>/dev/null; then \
|
||||
## This code is being executed by the first process.
|
||||
rm -f %D%/scan.stamp; \
|
||||
$(MAKE) $(AM_MAKEFLAGS) %D%/scan.stamp; \
|
||||
result=$$?; rm -rf %D%/scan.lock; exit $$result; \
|
||||
else \
|
||||
## This code is being executed by the follower processes.
|
||||
## Wait until the first process is done.
|
||||
while test -d %D%/scan.lock; do sleep 1; done; \
|
||||
## Succeed if and only if the first process succeeded.
|
||||
test -f %D%/scan.stamp; \
|
||||
fi; \
|
||||
fi
|
||||
|
||||
%D%/scan.stamp: %D%/scan.l
|
||||
$(AM_V_LEX)rm -f $@ $@.tmp
|
||||
$(AM_V_at)$(MKDIR_P) %D%
|
||||
$(AM_V_at)touch $@.tmp
|
||||
## --header introduced in 2.5.6, renamed as --header-file in 2.6.4.
|
||||
## Backward compatibility ensured since --header is an unambiguous prefix.
|
||||
$(AM_V_at)$(LEX) $(AM_LFLAGS) $(LFLAGS) -o%D%/scan.c --header=%D%/scan.h $(srcdir)/%D%/scan.l
|
||||
$(AM_V_at)mv $@.tmp $@
|
||||
|
||||
|
||||
EXTRA_DIST += %D%/reccalc.test %D%/scan.l
|
||||
dist_reccalc_DATA = %D%/parse.y %D%/scan.l %D%/Makefile %D%/README.md
|
||||
CLEANFILES += %D%/parse.[ch] %D%/parse.output %D%/scan.[ch] %D%/*.stamp
|
||||
CLEANDIRS += %D%/*.dSYM
|
||||
@@ -0,0 +1,220 @@
|
||||
/* Parser for reccalc. -*- C -*-
|
||||
|
||||
Copyright (C) 2019-2021 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of Bison, the GNU Compiler Compiler.
|
||||
|
||||
This program 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.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
// Prologue (directives).
|
||||
%expect 0
|
||||
|
||||
// Emitted in the header file, before the definition of YYSTYPE.
|
||||
%code requires
|
||||
{
|
||||
#ifndef YY_TYPEDEF_YY_SCANNER_T
|
||||
# define YY_TYPEDEF_YY_SCANNER_T
|
||||
typedef void* yyscan_t;
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
// Whether to print the intermediate results.
|
||||
int verbose;
|
||||
// Value of the last computation.
|
||||
int value;
|
||||
// Number of errors.
|
||||
int nerrs;
|
||||
} result;
|
||||
}
|
||||
|
||||
// Emitted in the header file, after the definition of YYSTYPE.
|
||||
%code provides
|
||||
{
|
||||
// Tell Flex the expected prototype of yylex.
|
||||
// The scanner argument must be named yyscanner.
|
||||
#define YY_DECL \
|
||||
yytoken_kind_t yylex (YYSTYPE* yylval, yyscan_t yyscanner, result *res)
|
||||
YY_DECL;
|
||||
|
||||
void yyerror (yyscan_t scanner, result *res, const char *msg, ...);
|
||||
}
|
||||
|
||||
// Emitted on top of the implementation file.
|
||||
%code top
|
||||
{
|
||||
#include <stdarg.h> // va_list.
|
||||
#include <stdio.h> // printf.
|
||||
#include <stdlib.h> // getenv.
|
||||
}
|
||||
|
||||
%code
|
||||
{
|
||||
result parse_string (const char* cp);
|
||||
result parse (void);
|
||||
}
|
||||
|
||||
// Include the header in the implementation rather than duplicating it.
|
||||
%define api.header.include {"parse.h"}
|
||||
|
||||
// Don't share global variables between the scanner and the parser.
|
||||
%define api.pure full
|
||||
|
||||
// To avoid name clashes (e.g., with C's EOF) prefix token definitions
|
||||
// with TOK_ (e.g., TOK_EOF).
|
||||
%define api.token.prefix {TOK_}
|
||||
|
||||
// Generate YYSTYPE from the types assigned to symbols.
|
||||
%define api.value.type union
|
||||
|
||||
// Error messages with "unexpected XXX, expected XXX...".
|
||||
%define parse.error detailed
|
||||
|
||||
// Enable run-time traces (yydebug).
|
||||
%define parse.trace
|
||||
|
||||
// Generate the parser description file (parse.output).
|
||||
%verbose
|
||||
|
||||
// Scanner and error count are exchanged between main, yyparse and yylex.
|
||||
%param {yyscan_t scanner}{result *res}
|
||||
|
||||
%token
|
||||
PLUS "+"
|
||||
MINUS "-"
|
||||
STAR "*"
|
||||
SLASH "/"
|
||||
EOL "end-of-line"
|
||||
EOF 0 "end-of-file"
|
||||
;
|
||||
|
||||
%token <int> NUM "number"
|
||||
%type <int> exp
|
||||
%printer { fprintf (yyo, "%d", $$); } <int>
|
||||
|
||||
%token <char*> STR "string"
|
||||
%printer { fprintf (yyo, "\"%s\"", $$); } <char*>
|
||||
%destructor { free ($$); } <char*>
|
||||
|
||||
// Precedence (from lowest to highest) and associativity.
|
||||
%left "+" "-"
|
||||
%left "*" "/"
|
||||
%precedence UNARY
|
||||
|
||||
%%
|
||||
// Rules.
|
||||
input:
|
||||
line
|
||||
| input line
|
||||
;
|
||||
|
||||
line:
|
||||
exp eol
|
||||
{
|
||||
res->value = $exp;
|
||||
if (res->verbose)
|
||||
printf ("%d\n", $exp);
|
||||
}
|
||||
| error eol
|
||||
{
|
||||
yyerrok;
|
||||
}
|
||||
;
|
||||
|
||||
eol:
|
||||
EOF
|
||||
| EOL
|
||||
;
|
||||
|
||||
exp:
|
||||
NUM { $$ = $1; }
|
||||
| exp "+" exp { $$ = $1 + $3; }
|
||||
| exp "-" exp { $$ = $1 - $3; }
|
||||
| exp "*" exp { $$ = $1 * $3; }
|
||||
| exp "/" exp
|
||||
{
|
||||
if ($3 == 0)
|
||||
{
|
||||
yyerror (scanner, res, "invalid division by zero");
|
||||
YYERROR;
|
||||
}
|
||||
else
|
||||
$$ = $1 / $3;
|
||||
}
|
||||
| "+" exp %prec UNARY { $$ = + $2; }
|
||||
| "-" exp %prec UNARY { $$ = - $2; }
|
||||
| STR
|
||||
{
|
||||
result r = parse_string ($1);
|
||||
free ($1);
|
||||
if (r.nerrs)
|
||||
{
|
||||
res->nerrs += r.nerrs;
|
||||
YYERROR;
|
||||
}
|
||||
else
|
||||
$$ = r.value;
|
||||
}
|
||||
;
|
||||
|
||||
%%
|
||||
// Epilogue (C code).
|
||||
#include "scan.h"
|
||||
|
||||
result
|
||||
parse (void)
|
||||
{
|
||||
yyscan_t scanner;
|
||||
yylex_init (&scanner);
|
||||
result res = {1, 0, 0};
|
||||
yyparse (scanner, &res);
|
||||
yylex_destroy (scanner);
|
||||
return res;
|
||||
}
|
||||
|
||||
result
|
||||
parse_string (const char *str)
|
||||
{
|
||||
yyscan_t scanner;
|
||||
yylex_init (&scanner);
|
||||
YY_BUFFER_STATE buf = yy_scan_string (str ? str : "", scanner);
|
||||
result res = {0, 0, 0};
|
||||
yyparse (scanner, &res);
|
||||
yy_delete_buffer (buf, scanner);
|
||||
yylex_destroy (scanner);
|
||||
return res;
|
||||
}
|
||||
|
||||
void
|
||||
yyerror (yyscan_t scanner, result *res,
|
||||
const char *msg, ...)
|
||||
{
|
||||
(void) scanner;
|
||||
va_list args;
|
||||
va_start (args, msg);
|
||||
vfprintf (stderr, msg, args);
|
||||
va_end (args);
|
||||
fputc ('\n', stderr);
|
||||
res->nerrs += 1;
|
||||
}
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
// Possibly enable parser runtime debugging.
|
||||
yydebug = !!getenv ("YYDEBUG");
|
||||
result res = parse ();
|
||||
// Exit on failure if there were errors.
|
||||
return !!res.nerrs;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
#! /bin/sh
|
||||
|
||||
# Copyright (C) 2018-2021 Free Software Foundation, Inc.
|
||||
#
|
||||
# This program 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.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
(seq 0) >/dev/null 2>&1 || skip "gimme one seq"
|
||||
|
||||
cat >input <<EOF
|
||||
1+2*3
|
||||
EOF
|
||||
run 0 '7'
|
||||
|
||||
cat >input <<EOF
|
||||
(1+2) * 3
|
||||
EOF
|
||||
run 0 '9'
|
||||
run -noerr 0 '9' -p
|
||||
|
||||
cat >input <<EOF
|
||||
(((1)+(2))*((3)+(4)))
|
||||
EOF
|
||||
run 0 '21'
|
||||
|
||||
# Some really deep computation.
|
||||
# for 4, gives 4 + (3 + (2 + (1 + (- (4 * (4 + 1)) / 2)))).
|
||||
n=100
|
||||
for i in $(seq 0 $n)
|
||||
do
|
||||
if [ "$i" -eq 0 ]; then
|
||||
input="- ($n * ($n + 1)) / 2"
|
||||
else
|
||||
input="$i + ($input)"
|
||||
fi
|
||||
done
|
||||
echo "$input" > input
|
||||
run 0 '0'
|
||||
|
||||
cat >input <<EOF
|
||||
() + ()
|
||||
EOF
|
||||
run 1 'err: syntax error, unexpected end-of-file, expecting + or - or number or string'
|
||||
|
||||
cat >input <<EOF
|
||||
1 + $
|
||||
EOF
|
||||
run 1 'err: syntax error, invalid character: $
|
||||
err: syntax error, unexpected end-of-line, expecting + or - or number or string'
|
||||
@@ -0,0 +1,108 @@
|
||||
/* Scanner for reccalc. -*- C -*-
|
||||
|
||||
Copyright (C) 2019-2021 Free Software Foundation, Inc.
|
||||
|
||||
This file is part of Bison, the GNU Compiler Compiler.
|
||||
|
||||
This program 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.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Prologue (directives). -*- C -*- */
|
||||
|
||||
/* Disable Flex features we don't need, to avoid warnings. */
|
||||
%option nodefault noinput nounput noyywrap
|
||||
|
||||
%option reentrant
|
||||
|
||||
%{
|
||||
#include <assert.h>
|
||||
#include <limits.h> /* INT_MIN */
|
||||
#include <stdlib.h> /* strtol */
|
||||
|
||||
#include "parse.h"
|
||||
%}
|
||||
|
||||
%x SC_STRING
|
||||
|
||||
%%
|
||||
%{
|
||||
// Number of opened parentheses.
|
||||
int nesting = 0;
|
||||
// A buffer storing the text inside the outer parentheses.
|
||||
char *str = NULL;
|
||||
// Its allocated size.
|
||||
int capacity = 0;
|
||||
// Its used size.
|
||||
int size = 0;
|
||||
#define STR_APPEND() \
|
||||
do { \
|
||||
if (capacity < size + yyleng + 1) \
|
||||
{ \
|
||||
do \
|
||||
capacity = capacity ? 2 * capacity : 128; \
|
||||
while (capacity < size + yyleng + 1); \
|
||||
str = realloc (str, (size_t) capacity); \
|
||||
} \
|
||||
memcpy (str + size, yytext, (size_t) yyleng); \
|
||||
size += yyleng; \
|
||||
assert (size < capacity); \
|
||||
} while (0)
|
||||
%}
|
||||
|
||||
// Rules.
|
||||
|
||||
"+" return TOK_PLUS;
|
||||
"-" return TOK_MINUS;
|
||||
"*" return TOK_STAR;
|
||||
"/" return TOK_SLASH;
|
||||
|
||||
"(" nesting += 1; BEGIN SC_STRING;
|
||||
|
||||
/* Scan an integer. */
|
||||
[0-9]+ {
|
||||
errno = 0;
|
||||
long n = strtol (yytext, NULL, 10);
|
||||
if (! (INT_MIN <= n && n <= INT_MAX && errno != ERANGE))
|
||||
yyerror (yyscanner, res, "integer is out of range");
|
||||
yylval->TOK_NUM = (int) n;
|
||||
return TOK_NUM;
|
||||
}
|
||||
|
||||
/* Ignore white spaces. */
|
||||
[ \t]+ continue;
|
||||
|
||||
"\n" return TOK_EOL;
|
||||
|
||||
. yyerror (yyscanner, res, "syntax error, invalid character: %c", yytext[0]);
|
||||
|
||||
<SC_STRING>
|
||||
{
|
||||
"("+ nesting += yyleng; STR_APPEND ();
|
||||
")" {
|
||||
if (!--nesting)
|
||||
{
|
||||
BEGIN INITIAL;
|
||||
if (str)
|
||||
str[size] = 0;
|
||||
yylval->TOK_STR = str;
|
||||
return TOK_STR;
|
||||
}
|
||||
else
|
||||
STR_APPEND ();
|
||||
}
|
||||
[^()]+ STR_APPEND ();
|
||||
}
|
||||
|
||||
<<EOF>> return TOK_EOF;
|
||||
%%
|
||||
/* Epilogue (C code). */
|
||||
@@ -0,0 +1,20 @@
|
||||
# This Makefile is designed to be simple and readable. It does not
|
||||
# aim at portability. It requires GNU Make.
|
||||
|
||||
BASE = rpcalc
|
||||
BISON = bison
|
||||
|
||||
all: $(BASE)
|
||||
|
||||
%.c %.html %.gv: %.y
|
||||
$(BISON) $(BISONFLAGS) --html --graph -o $*.c $<
|
||||
|
||||
%: %.c
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
run: $(BASE)
|
||||
@echo "Type arithmetic expressions in Reverse Polish Notation. Quit with ctrl-d."
|
||||
./$<
|
||||
|
||||
clean:
|
||||
rm -f $(BASE) $(BASE).c $(BASE).html $(BASE).xml $(BASE).gv
|
||||
@@ -0,0 +1,45 @@
|
||||
## Copyright (C) 2005-2006, 2008-2015, 2018-2021 Free Software
|
||||
## Foundation, Inc.
|
||||
##
|
||||
## This program 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.
|
||||
##
|
||||
## This program is distributed in the hope that it will be useful,
|
||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU General Public License for more details.
|
||||
##
|
||||
## You should have received a copy of the GNU General Public License
|
||||
## along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
## -------------------- ##
|
||||
## Building & testing. ##
|
||||
## -------------------- ##
|
||||
|
||||
BUILT_SOURCES += $(rpcalc_sources)
|
||||
CLEANFILES += %D%/rpcalc.[ch] %D%/rpcalc.output
|
||||
CLEANDIRS += %D%/*.dSYM
|
||||
|
||||
rpcalc_extracted = %D%/rpcalc.y
|
||||
rpcalc_sources = $(rpcalc_extracted)
|
||||
extracted += $(rpcalc_extracted)
|
||||
|
||||
check_PROGRAMS += %D%/rpcalc
|
||||
nodist_%C%_rpcalc_SOURCES = $(rpcalc_sources)
|
||||
%D%/rpcalc.c: $(dependencies)
|
||||
# Don't use gnulib's system headers.
|
||||
%C%_rpcalc_CPPFLAGS = -I$(top_builddir)/%D%
|
||||
%C%_rpcalc_CFLAGS = $(TEST_CFLAGS)
|
||||
%C%_rpcalc_LDADD = -lm
|
||||
|
||||
dist_TESTS += %D%/rpcalc.test
|
||||
|
||||
## ------------ ##
|
||||
## Installing. ##
|
||||
## ------------ ##
|
||||
|
||||
rpcalcdir = $(docdir)/%D%
|
||||
rpcalc_DATA = $(rpcalc_extracted)
|
||||
dist_rpcalc_DATA = %D%/Makefile
|
||||
@@ -0,0 +1,46 @@
|
||||
#! /bin/sh
|
||||
|
||||
# Copyright (C) 2005-2015, 2018-2021 Free Software Foundation, Inc.
|
||||
#
|
||||
# This program 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.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
cat >input <<EOF
|
||||
1 2 3 * +
|
||||
EOF
|
||||
run 0 7
|
||||
|
||||
cat >input <<EOF
|
||||
1.1 2.2 3.3 * +
|
||||
EOF
|
||||
run 0 8.36
|
||||
|
||||
cat >input <<EOF
|
||||
1 2 + 3 *
|
||||
EOF
|
||||
run 0 9
|
||||
|
||||
cat >input <<EOF
|
||||
1 2 3 4 5 6 7 8 9 * * * * * * * *
|
||||
EOF
|
||||
run 0 362880
|
||||
|
||||
cat >input <<EOF
|
||||
3 7 + 3 4 5 * + - n
|
||||
EOF
|
||||
run 0 13
|
||||
|
||||
cat >input <<EOF
|
||||
3 4 ^
|
||||
EOF
|
||||
run 0 81
|
||||
@@ -0,0 +1,100 @@
|
||||
/* Parser for rpcalc. -*- C -*-
|
||||
|
||||
Copyright (C) 1988-1993, 1995, 1998-2015, 2018-2021 Free Software
|
||||
Foundation, Inc.
|
||||
|
||||
This file is part of Bison, the GNU Compiler Compiler.
|
||||
|
||||
This program 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.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
/* Reverse Polish Notation calculator. */
|
||||
|
||||
%{
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
int yylex (void);
|
||||
void yyerror (char const *);
|
||||
%}
|
||||
|
||||
%define api.value.type {double}
|
||||
%token NUM
|
||||
|
||||
%% /* Grammar rules and actions follow. */
|
||||
|
||||
input:
|
||||
%empty
|
||||
| input line
|
||||
;
|
||||
|
||||
line:
|
||||
'\n'
|
||||
| exp '\n' { printf ("%.10g\n", $1); }
|
||||
;
|
||||
|
||||
exp:
|
||||
NUM
|
||||
| exp exp '+' { $$ = $1 + $2; }
|
||||
| exp exp '-' { $$ = $1 - $2; }
|
||||
| exp exp '*' { $$ = $1 * $2; }
|
||||
| exp exp '/' { $$ = $1 / $2; }
|
||||
| exp exp '^' { $$ = pow ($1, $2); } /* Exponentiation */
|
||||
| exp 'n' { $$ = -$1; } /* Unary minus */
|
||||
;
|
||||
%%
|
||||
|
||||
/* The lexical analyzer returns a double floating point
|
||||
number on the stack and the token NUM, or the numeric code
|
||||
of the character read if not a number. It skips all blanks
|
||||
and tabs, and returns 0 for end-of-input. */
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int
|
||||
yylex (void)
|
||||
{
|
||||
int c = getchar ();
|
||||
/* Skip white space. */
|
||||
while (c == ' ' || c == '\t')
|
||||
c = getchar ();
|
||||
/* Process numbers. */
|
||||
if (c == '.' || isdigit (c))
|
||||
{
|
||||
ungetc (c, stdin);
|
||||
if (scanf ("%lf", &yylval) != 1)
|
||||
abort ();
|
||||
return NUM;
|
||||
}
|
||||
/* Return end-of-input. */
|
||||
else if (c == EOF)
|
||||
return YYEOF;
|
||||
/* Return a single char. */
|
||||
else
|
||||
return c;
|
||||
}
|
||||
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
return yyparse ();
|
||||
}
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
/* Called by yyparse on error. */
|
||||
void
|
||||
yyerror (char const *s)
|
||||
{
|
||||
fprintf (stderr, "%s\n", s);
|
||||
}
|
||||
Reference in New Issue
Block a user