diff options
author | Rob Pike <r@golang.org> | 2012-01-29 09:19:05 -0800 |
---|---|---|
committer | Rob Pike <r@golang.org> | 2012-01-29 09:19:05 -0800 |
commit | e783e9ee47804dbd6d8d48a0b99570060b478231 (patch) | |
tree | 054a496d4b2500a4f558091fab9c3ffc7c4dab00 /src/cmd/yacc | |
parent | 1fd238ff924cd14f34773ad4c9983b19347dd4d3 (diff) | |
download | go-e783e9ee47804dbd6d8d48a0b99570060b478231.tar.gz |
cmd/go: first piece of tool rearrangement
1) create go-tool dir in make.bash
2) clean up stale binaries in make.bash
3) add 'tool' command to go
4) convert goyacc->yacc as a first test tool
Since goyacc stands alone, it's a safe trial.
R=rsc
CC=golang-dev
http://codereview.appspot.com/5576061
Diffstat (limited to 'src/cmd/yacc')
-rw-r--r-- | src/cmd/yacc/Makefile | 18 | ||||
-rw-r--r-- | src/cmd/yacc/doc.go | 48 | ||||
-rw-r--r-- | src/cmd/yacc/units.txt | 576 | ||||
-rw-r--r-- | src/cmd/yacc/units.y | 759 | ||||
-rw-r--r-- | src/cmd/yacc/yacc.go | 3324 |
5 files changed, 4725 insertions, 0 deletions
diff --git a/src/cmd/yacc/Makefile b/src/cmd/yacc/Makefile new file mode 100644 index 000000000..6ce9d54fb --- /dev/null +++ b/src/cmd/yacc/Makefile @@ -0,0 +1,18 @@ +# Copyright 2009 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +include ../../Make.inc + +TARG=yacc +GOFILES=\ + yacc.go\ + +include ../../Make.tool + +units: yacc units.y + ./yacc -p units_ units.y + $(GC) $(GCFLAGS) $(GCIMPORTS) y.go + $(LD) -o units y.$O + +CLEANFILES += units y.go y.output diff --git a/src/cmd/yacc/doc.go b/src/cmd/yacc/doc.go new file mode 100644 index 000000000..9874a2ae2 --- /dev/null +++ b/src/cmd/yacc/doc.go @@ -0,0 +1,48 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* + +Yacc is a version of yacc for Go. It is run with the command + go tool yacc args... +It is written in Go and generates parsers written in Go. + +It is largely transliterated from the Inferno version written in Limbo +which in turn was largely transliterated from the Plan 9 version +written in C and documented at + + http://plan9.bell-labs.com/magic/man2html/1/yacc + +Adepts of the original yacc will have no trouble adapting to this +form of the tool. + +The file units.y in this directory is a yacc grammar for a version of +the Unix tool units, also written in Go and largely transliterated +from the Plan 9 C version. It needs the flag "-p units_" (see +below). + +The generated parser is reentrant. Parse expects to be given an +argument that conforms to the following interface: + + type yyLexer interface { + Lex(lval *yySymType) int + Error(e string) + } + +Lex should return the token identifier, and place other token +information in lval (which replaces the usual yylval). +Error is equivalent to yyerror in the original yacc. + +Code inside the parser may refer to the variable yylex, +which holds the yyLexer passed to Parse. + +Multiple grammars compiled into a single program should be placed in +distinct packages. If that is impossible, the "-p prefix" flag to +yacc sets the prefix, by default yy, that begins the names of +symbols, including types, the parser, and the lexer, generated and +referenced by yacc's generated code. Setting it to distinct values +allows multiple grammars to be placed in a single package. + +*/ +package documentation diff --git a/src/cmd/yacc/units.txt b/src/cmd/yacc/units.txt new file mode 100644 index 000000000..ddb2bc294 --- /dev/null +++ b/src/cmd/yacc/units.txt @@ -0,0 +1,576 @@ +/ Plan 9's /lib/units +/ http://plan9.bell-labs.com/sources/plan9/lib/units +/ +/ Copyright (C) 2003, Lucent Technologies Inc. and others. All Rights Reserved. +/ Distributed under the terms of the Lucent Public License Version 1.02 +/ See http://plan9.bell-labs.com/plan9/license.html +/ +/order of evaluation +/ + - +/ * / +/ juxtaposition (meaning *) +/ ¹ ² ³ ^ +/ | (meaning /) +/ name number () + +/dimensions +m # +kg # +sec # +coul # +candela # +$ # +radian # +bit # +erlang # +°K # +°C # +°F # + +/constants + +π 3.14159265358979323846 +pi π +c 2.997925e+8 m/sec +g 9.80665 m/sec² +au 1.49597871e+11 m +mole 6.022169e+23 +e 1.6021917e-19 coul +energy c² +force g +mercury 1.33322e+5 kg/m²sec² +hg mercury +h 6.62620e-34 m²kg/sec +ℏ h/2 π +hbar ℏ +nonillion 1e30 +octillion 1e27 +septillion 1e24 +sextillion 1e21 +pentillion 1e18 +quadrillion 1e15 +trillion 1e12 +billion 1e9 +million 1e6 +thousand 1e3 +hundred 1e2 + +/dimensionless + +° 1|180 π radian +degree ° +circle 2 π radian +turn 2 π radian +grad .9 ° +arcdeg 1 ° +arcmin 1|60 ° +arcsec 1|3600 ° +ccs 1|36 erlang + +steradian radian² +sphere 4 π steradian +sr steradian +giga 1024 1024 1024 + +/Time + +second sec +s sec +minute 60 sec +min minute +hour 60 min +hr hour +day 24 hr +da day +week 7 day +year 365.24219879 day +yr year +month 1|12 year +ms millisec +us microsec + +/Mass + +gram millikg +gm gram +mg milligram +metricton kilokg + +/Avoirdupois + +lb .45359237 kg +lbf lb g +pound lb +ounce 1|16 lb +oz ounce +dram 1|16 oz +dr dram +grain 1|7000 lb +gr grain +shortton 2000 lb +ton shortton +longton 2240 lb + +/Apothecary + +scruple 20 grain +apdram 60 grain +apounce 480 grain +troyounce apounce +appound 5760 grain +troypound appound + +/Length + +meter m +cm centimeter +mm millimeter +km kilometer +nm nanometer +micron micrometer +µ micrometer +Å decinanometer +angstrom Å + +inch 2.54 cm +" inch +in inch +inches inch +' 12" +foot 12 in +feet foot +ft foot +yard 3 ft +yd yard +rod 5.5 yd +rd rod +mile 5280 ft +mi mile + +british 1200|3937 m/ft +nmile 1852 m + +acre 4840 yd² + +cc cm³ +liter kilocc +ml milliliter + +/US Liquid + +gallon 231 in³ +imperial 1.20095 +epa 0.8 +gal gallon +quart 1|4 gal +qt quart +pint 1|2 qt +pt pint + +floz 1|16 pt +fldr 1|8 floz + +/US Dry + +dry 268.8025 in³/gallon +peck 8 dry quart +pk peck +bushel 4 peck +bu bushel + +/British + +brgallon 277.420 in³ +brquart 1|4 brgallon +brpint 1|2 brquart +brfloz 1|20 brpint +brpeck 554.84 in³ +brbushel 4 brpeck + +/Energy Work + +newton kg m/sec² +nt newton +joule nt m +cal 4.1868 joule + +/Electrical + +coulomb coul +ampere coul/sec +amp ampere +watt joule/sec +volt watt/amp +Ω volt/amp +ohm Ω +mho 1/Ω +farad coul/volt +henry sec²/farad +weber volt sec + +/Light + +cd candela +lumen cd sr +lux cd sr/m² + +/ MONEY DATE +/ Thu Sep 10 2009 + +argentinapeso 1 | 0.2595 $ +australiadollar 1 | 0.8618 $ +boliviaboliviano 1 | 0.1425 $ +brazilreal 1 | 0.5522 $ +britainpound 1 | 1.6651 $ +canadadollar 1 | 0.9277 $ +chilepeso 1 | 0.0018 $ +chinayuan 1 | 0.1464 $ +colombiapeso 1 | 0.0005 $ +czechkoruna 1 | 0.0572 $ +denmarkkrone 1 | 0.1958 $ +dominicanpeso 1 | 0.0278 $ +egyptpound 1 | 0.181 $ +elsalvadorcolon 1 | 0.1143 $ +europeuro 1 | 1.4577 $ +guatemalaquetzal 1 | 0.121 $ +honduraslempira 1 | 0.0529 $ +hongkongdollar 1 | 0.129 $ +hungaryforint 1 | 0.0054 $ +indiarupee 1 | 0.0207 $ +indonesiarupiah 1 | 0.0001 $ +israelshekel 1 | 0.2643 $ +japanyen 1 | 0.0109 $ +kenyashilling 1 | 0.0132 $ +kuwaitdinar 1 | 3.4854 $ +lebanonpound 1 | 0.0007 $ +malaysiaringgit 1 | 0.286 $ +mexicopeso 1 | 0.0748 $ +newzealanddollar 1 | 0.7028 $ +nicaraguacordoba 1 | 0.0487 $ +norwaykrone 1 | 0.1681 $ +pakistanrupee 1 | 0.0121 $ +paraguayguarani 1 | 0.0002 $ +perunewsol 1 | 0.3384 $ +philippinespeso 1 | 0.0207 $ +polandzloty 1 | 0.352 $ +russiaruble 1 | 0.0324 $ +saudiarabiariyal 1 | 0.2666 $ +singaporedollar 1 | 0.7018 $ +slovakkoruna 1 | 0.0484 $ +southafricarand 1 | 0.132 $ +southkoreawon 1 | 0.0008 $ +swedenkrona 1 | 0.1429 $ +switzerlandfranc 1 | 0.9627 $ +taiwandollar 1 | 0.0306 $ +thailandbaht 1 | 0.0294 $ +turkeynewlira 1 | 0.6678 $ +uaedirham 1 | 0.2722 $ +uruguaynewpeso 1 | 0.0451 $ +vietnamdong 1 | 0.0001 $ + +/ END MONEY + +€ europeuro +£ britainpound +¥ japanyen +dollar $ + +baht thailandbaht +brpound britainpound +dirham uaedirham +euro europeuro +forint hungaryforint +krona swedenkrona +peso mexicopeso +rand southafricarand +real brazilreal +yuan chinayuan +ringgit malaysiaringgit +riyal saudiarabiariyal +ruble russiaruble +rupee indiarupee +rupiah indonesiarupiah +shekel israelshekel +sol perunewsol +won southkoreawon +yen japanyen +zloty polandzloty + +usdollar dollar +sterling britainpound | pound +poundsterling britainpound + +/bits + +baud bit/sec +byte 8 bit +short 2 byte +long 4 byte +vlong 8 bytes +frame 2352 byte + +/Australian liquid measure + +pony 7 brfloz +midie 10 brfloz +pot midie +handle midie +schooner 15 brfloz +jug 40 brfloz +resch midie +alf midie +tinny 13 brfloz +stubby tinny +twisty 250 ml +longneck 2 tinny +slab 24 tinny +sixpack 6 tinny +nip brfloz + +/wine +winebottle 750 ml +balthazar 16 winebottle +jeroboam 4 winebottle +magnum 2 winebottle +mathusalem 8 winebottle +methuselah 8 winebottle +nebuchadnezzar 20 winebottle +rehoboam 6 winebottle +salmanazar 12 winebottle +split 0.25 winebottle +jigger 1.5 floz + +/Trivia + +% 1|100 +admiraltyknot 6080 ft/hr +ε₀ (1e-9/36π) farad/m +α (1/4π ε₀) e²/ℏ c +alpha α +apostilb cd/π m² +are 1e+2 m² +arpentcan 27.52 mi +arpentlin 191.835 ft +astronomicalunit au +atmosphere 1.01325e+5 nt/m² +atm atmosphere +atomicmassunit 1.66044e-27 kg +amu atomicmassunit +bag 94 lb +bakersdozen 13 +bar 1e+5 nt/m² +barie 1e-1 nt/m² +barleycorn 1|3 in +barn 1e-28 m² +barrel 42 gal +barye 1e-1 nt/m² +bev 1e+9 e volt +biot 10 amp +blondel cd/π m² +boardfoot 144 in³ +bolt 40 yd +bottommeasure 1|40 in +britishthermalunit 1.05506e+3 joule +btu britishthermalunit +quad 1.0e+15 btu +refrigeration 12000 btu/ton hour +buck dollar +cable 720 ft +caliber 1e-2 in +calorie cal +carat 205 mg +cent centidollar +cental 100 lb +centesimalminute 1e-2 grad +centesimalsecond 1e-4 grad +century 100 year +cfs ft³/sec +chain 66 ft +circularinch 1|4 π in² +circularmil 1e-6|4 π in² +clusec 1e-8 mm hg m³/s +coomb 4 bu +cord 128 ft³ +cordfoot cord +crith 9.06e-2 gm +cubit 18 in +cup 1|2 pt +curie 3.7e+10/sec +cusec ft³/sec +dalton amu +decade 10 yr +degK °K +degC °C +degF °F +dipotre 1/m +displacementton 35 ft³ +doppelzentner 100 kg +dozen 12 +drop .03 cm³ +dyne cm gm/sec² +electronvolt e volt +ell 45 in +engineerschain 100 ft +engineerslink 100|100 ft +equivalentfootcandle lumen/π ft² +equivalentlux lumen/π m² +equivalentphot cd/π cm² +erg cm²gm/sec² +ev e volt +faraday 9.652e+4 coul +fathom 6 ft +fermi 1e-15 m +fifth 4|5 qt +fin 5 dollar +finger 7|8 in +firkin 9 gal +footcandle lumen/ft² +footlambert cd/π ft² +fortnight 14 da +franklin 3.33564e-10 coul +frigorie kilocal +furlong 220 yd +galileo 1e-2 m/sec² +gamma 1e-9 weber/m² +gauss 1e-4 weber/m² +geodeticfoot british ft +geographicalmile 1852 m +gilbert 7.95775e-1 amp +gill 1|4 pt +gross 144 +gunterschain 22 yd +hand 4 in +hectare 1e+4 m² +hefnercandle .92 cd +hertz 1/sec +hogshead 2 barrel +hd hogshead +homestead 1|4 mi² +horsepower 550 ft lb g/sec +hp horsepower +hyl gm force sec²/m +hz 1/sec +imaginarycubicfoot 1.4 ft³ +karat 1|24 +kcal kilocal +kcalorie kilocal +kev 1e+3 e volt +key kg +khz 1e+3/sec +kilderkin 18 gal +knot nmile/hr +kwh kilowatt hour +lambert cd/π cm² +langley cal/cm² +last 80 bu +league 3 mi +lightyear c yr +ly lightyear +lightsecond c sec +line 1|12 in +link 66|100 ft +longhundredweight 112 lb +longquarter 28 lb +lusec 1e-6 mm hg m³/s +mach 331.46 m/sec +marineleague 3 nmile +maxwell 1e-8 weber +metriccarat 200 mg +mev 1e+6 e volt +mgd megagal/day +mh millihenry +mhz 1e+6/sec +mil 1e-3 in +millenium 1000 year +minersinch 1.5 ft³/min +minim 1|60 fldr +mo month +mpg mile/gal +mph mile/hr +nail 1|16 yd +nauticalmile nmile +nit cd/m² +noggin 1|8 qt +nox 1e-3 lux +ns nanosec +oersted 2.5e+2 amp/m π +oe oersted +pace 36 in +palm 3 in +parasang 3.5 mi +parsec au radian/arcsec +pascal nt/m² +pc parsec +pennyweight 1|20 oz +percent % +perch rd +pf picofarad +phot lumen/cm² +pica 1|6 in +pieze 1e+3 nt/m² +pipe 4 barrel +point 1|72 in +poise gm/cm sec +pole rd +poundal ft lb/sec² +pdl poundal +proof 1/200 +psi lb g/in² +quarter 9 in +quartersection 1|4 mi² +quintal 100 kg +quire 25 +rad 100 erg/gm +ream 500 +registerton 100 ft³ +rhe 10 m²/nt sec +rontgen 2.58e-4 curie/kg +rood 1.21e+3 yd +rope 20 ft +rutherford 1e+6/sec +rydberg 1.36054e+1 ev +sabin 1 ft² +sack 3 bu +seam 8 bu +section mi² +shippington 40 ft³ +shorthundredweight 100 lb +shortquarter 25 lb +siemens 1/Ω +σ 5.66956e-5 erg/cm² °K^4 sec +sigma σ +skein 120 yd +skot 1e-3 apostilb +slug lb g sec²/ft +span 9 in +spat 4 π sr +spindle 14400 yd +square 100 ft² +squidge 1|972 inch +catsquidge 1|432 inch +stere m³ +sthene 1e+3 nt +stilb cd/cm² +stoke 1e-4 m²/sec +stone 14 lb +strike 2 bu +surveyfoot british ft +surveyorschain 66 ft +surveyorslink 66|100 ft +tablespoon 4 fldr +teaspoon 4|3 fldr +tesla weber/m² +therm 1e+5 btu +thermie 1e+6 cal +timberfoot ft³ +tnt 4.6e+6 m²/sec² +tonne 1e+6 gm +torr mm hg +township 36 mi² +tun 8 barrel +water .22491|2.54 kg/m²sec² +wey 40 bu +weymass 252 lb +Xunit 1.00202e-13 m +k 1.38047e-16 erg/°K +foal 9223372036854775807 diff --git a/src/cmd/yacc/units.y b/src/cmd/yacc/units.y new file mode 100644 index 000000000..f10cb7c7d --- /dev/null +++ b/src/cmd/yacc/units.y @@ -0,0 +1,759 @@ +// Derived from Plan 9's /sys/src/cmd/units.y +// http://plan9.bell-labs.com/sources/plan9/sys/src/cmd/units.y +// +// Copyright (C) 2003, Lucent Technologies Inc. and others. All Rights Reserved. +// Portions Copyright 2009 The Go Authors. All Rights Reserved. +// Distributed under the terms of the Lucent Public License Version 1.02 +// See http://plan9.bell-labs.com/plan9/license.html + +// Generate parser with prefix "units_": +// go tool yacc -p "units_" + +%{ + +// units.y +// example of a Go yacc program +// usage is +// go tool yacc -p "units_" units.y (produces y.go) +// 6g y.go +// 6l y.6 +// ./6.out $GOROOT/src/cmd/yacc/units +// you have: c +// you want: furlongs/fortnight +// * 1.8026178e+12 +// / 5.5474878e-13 +// you have: + +package main + +import ( + "flag" + "fmt" + "bufio" + "os" + "math" + "strconv" + "unicode/utf8" +) + +const ( + Ndim = 15 // number of dimensions + Maxe = 695 // log of largest number +) + +type Node struct { + vval float64 + dim [Ndim]int8 +} + +type Var struct { + name string + node Node +} + +var fi *bufio.Reader // input +var fund [Ndim]*Var // names of fundamental units +var line string // current input line +var lineno int // current input line number +var linep int // index to next rune in unput +var nerrors int // error count +var one Node // constant one +var peekrune rune // backup runt from input +var retnode1 Node +var retnode2 Node +var retnode Node +var sym string +var vflag bool +%} + +%union { + node Node + vvar *Var + numb int + vval float64 +} + +%type <node> prog expr expr0 expr1 expr2 expr3 expr4 + +%token <vval> VAL +%token <vvar> VAR +%token <numb> SUP +%% +prog: + ':' VAR expr + { + var f int + f = int($2.node.dim[0]) + $2.node = $3 + $2.node.dim[0] = 1 + if f != 0 { + Errorf("redefinition of %v", $2.name) + } else if vflag { + fmt.Printf("%v\t%v\n", $2.name, &$2.node) + } + } +| ':' VAR '#' + { + var f, i int + for i = 1; i < Ndim; i++ { + if fund[i] == nil { + break + } + } + if i >= Ndim { + Error("too many dimensions") + i = Ndim - 1 + } + fund[i] = $2 + f = int($2.node.dim[0]) + $2.node = one + $2.node.dim[0] = 1 + $2.node.dim[i] = 1 + if f != 0 { + Errorf("redefinition of %v", $2.name) + } else if vflag { + fmt.Printf("%v\t#\n", $2.name) + } + } +| ':' + { + } +| '?' expr + { + retnode1 = $2 + } +| '?' + { + retnode1 = one + } + +expr: + expr4 +| expr '+' expr4 + { + add(&$$, &$1, &$3) + } +| expr '-' expr4 + { + sub(&$$, &$1, &$3) + } + +expr4: + expr3 +| expr4 '*' expr3 + { + mul(&$$, &$1, &$3) + } +| expr4 '/' expr3 + { + div(&$$, &$1, &$3) + } + +expr3: + expr2 +| expr3 expr2 + { + mul(&$$, &$1, &$2) + } + +expr2: + expr1 +| expr2 SUP + { + xpn(&$$, &$1, $2) + } +| expr2 '^' expr1 + { + var i int + for i = 1; i < Ndim; i++ { + if $3.dim[i] != 0 { + Error("exponent has units") + $$ = $1 + break + } + } + if i >= Ndim { + i = int($3.vval) + if float64(i) != $3.vval { + Error("exponent not integral") + } + xpn(&$$, &$1, i) + } + } + +expr1: + expr0 +| expr1 '|' expr0 + { + div(&$$, &$1, &$3) + } + +expr0: + VAR + { + if $1.node.dim[0] == 0 { + Errorf("undefined %v", $1.name) + $$ = one + } else { + $$ = $1.node + } + } +| VAL + { + $$ = one + $$.vval = $1 + } +| '(' expr ')' + { + $$ = $2 + } +%% + +type UnitsLex int + +func (UnitsLex) Lex(yylval *units_SymType) int { + var c rune + var i int + + c = peekrune + peekrune = ' ' + +loop: + if (c >= '0' && c <= '9') || c == '.' { + goto numb + } + if ralpha(c) { + goto alpha + } + switch c { + case ' ', '\t': + c = getrune() + goto loop + case '×': + return '*' + case '÷': + return '/' + case '¹', 'ⁱ': + yylval.numb = 1 + return SUP + case '²', '': + yylval.numb = 2 + return SUP + case '³', '': + yylval.numb = 3 + return SUP + } + return int(c) + +alpha: + sym = "" + for i = 0; ; i++ { + sym += string(c) + c = getrune() + if !ralpha(c) { + break + } + } + peekrune = c + yylval.vvar = lookup(0) + return VAR + +numb: + sym = "" + for i = 0; ; i++ { + sym += string(c) + c = getrune() + if !rdigit(c) { + break + } + } + peekrune = c + f, err := strconv.ParseFloat(sym, 64) + if err != nil { + fmt.Printf("error converting %v\n", sym) + f = 0 + } + yylval.vval = f + return VAL +} + +func (UnitsLex) Error(s string) { + Errorf("syntax error, last name: %v", sym) +} + +func main() { + var file string + + flag.BoolVar(&vflag, "v", false, "verbose") + + flag.Parse() + + file = os.Getenv("GOROOT") + "/src/cmd/yacc/units.txt" + if flag.NArg() > 0 { + file = flag.Arg(0) + } + + f, err := os.Open(file) + if err != nil { + fmt.Fprintf(os.Stderr, "error opening %v: %v\n", file, err) + os.Exit(1) + } + fi = bufio.NewReader(f) + + one.vval = 1 + + /* + * read the 'units' file to + * develope a database + */ + lineno = 0 + for { + lineno++ + if readline() { + break + } + if len(line) == 0 || line[0] == '/' { + continue + } + peekrune = ':' + units_Parse(UnitsLex(0)) + } + + /* + * read the console to + * print ratio of pairs + */ + fi = bufio.NewReader(os.NewFile(0, "stdin")) + + lineno = 0 + for { + if (lineno & 1) != 0 { + fmt.Printf("you want: ") + } else { + fmt.Printf("you have: ") + } + if readline() { + break + } + peekrune = '?' + nerrors = 0 + units_Parse(UnitsLex(0)) + if nerrors != 0 { + continue + } + if (lineno & 1) != 0 { + if specialcase(&retnode, &retnode2, &retnode1) { + fmt.Printf("\tis %v\n", &retnode) + } else { + div(&retnode, &retnode2, &retnode1) + fmt.Printf("\t* %v\n", &retnode) + div(&retnode, &retnode1, &retnode2) + fmt.Printf("\t/ %v\n", &retnode) + } + } else { + retnode2 = retnode1 + } + lineno++ + } + fmt.Printf("\n") + os.Exit(0) +} + +/* + * all characters that have some + * meaning. rest are usable as names + */ +func ralpha(c rune) bool { + switch c { + case 0, '+', '-', '*', '/', '[', ']', '(', ')', + '^', ':', '?', ' ', '\t', '.', '|', '#', + '×', '÷', '¹', 'ⁱ', '²', '', '³', '': + return false + } + return true +} + +/* + * number forming character + */ +func rdigit(c rune) bool { + switch c { + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '.', 'e', '+', '-': + return true + } + return false +} + +func Errorf(s string, v ...interface{}) { + fmt.Printf("%v: %v\n\t", lineno, line) + fmt.Printf(s, v...) + fmt.Printf("\n") + + nerrors++ + if nerrors > 5 { + fmt.Printf("too many errors\n") + os.Exit(1) + } +} + +func Error(s string) { + Errorf("%s", s) +} + +func add(c, a, b *Node) { + var i int + var d int8 + + for i = 0; i < Ndim; i++ { + d = a.dim[i] + c.dim[i] = d + if d != b.dim[i] { + Error("add must be like units") + } + } + c.vval = fadd(a.vval, b.vval) +} + +func sub(c, a, b *Node) { + var i int + var d int8 + + for i = 0; i < Ndim; i++ { + d = a.dim[i] + c.dim[i] = d + if d != b.dim[i] { + Error("sub must be like units") + } + } + c.vval = fadd(a.vval, -b.vval) +} + +func mul(c, a, b *Node) { + var i int + + for i = 0; i < Ndim; i++ { + c.dim[i] = a.dim[i] + b.dim[i] + } + c.vval = fmul(a.vval, b.vval) +} + +func div(c, a, b *Node) { + var i int + + for i = 0; i < Ndim; i++ { + c.dim[i] = a.dim[i] - b.dim[i] + } + c.vval = fdiv(a.vval, b.vval) +} + +func xpn(c, a *Node, b int) { + var i int + + *c = one + if b < 0 { + b = -b + for i = 0; i < b; i++ { + div(c, c, a) + } + } else { + for i = 0; i < b; i++ { + mul(c, c, a) + } + } +} + +func specialcase(c, a, b *Node) bool { + var i int + var d, d1, d2 int8 + + d1 = 0 + d2 = 0 + for i = 1; i < Ndim; i++ { + d = a.dim[i] + if d != 0 { + if d != 1 || d1 != 0 { + return false + } + d1 = int8(i) + } + d = b.dim[i] + if d != 0 { + if d != 1 || d2 != 0 { + return false + } + d2 = int8(i) + } + } + if d1 == 0 || d2 == 0 { + return false + } + + if fund[d1].name == "°C" && fund[d2].name == "°F" && + b.vval == 1 { + for ll := 0; ll < len(c.dim); ll++ { + c.dim[ll] = b.dim[ll] + } + c.vval = a.vval*9./5. + 32. + return true + } + + if fund[d1].name == "°F" && fund[d2].name == "°C" && + b.vval == 1 { + for ll := 0; ll < len(c.dim); ll++ { + c.dim[ll] = b.dim[ll] + } + c.vval = (a.vval - 32.) * 5. / 9. + return true + } + return false +} + +func printdim(str string, d, n int) string { + var v *Var + + if n != 0 { + v = fund[d] + if v != nil { + str += fmt.Sprintf("%v", v.name) + } else { + str += fmt.Sprintf("[%d]", d) + } + switch n { + case 1: + break + case 2: + str += "²" + case 3: + str += "³" + default: + str += fmt.Sprintf("^%d", n) + } + } + return str +} + +func (n Node) String() string { + var str string + var f, i, d int + + str = fmt.Sprintf("%.7e ", n.vval) + + f = 0 + for i = 1; i < Ndim; i++ { + d = int(n.dim[i]) + if d > 0 { + str = printdim(str, i, d) + } else if d < 0 { + f = 1 + } + } + + if f != 0 { + str += " /" + for i = 1; i < Ndim; i++ { + d = int(n.dim[i]) + if d < 0 { + str = printdim(str, i, -d) + } + } + } + + return str +} + +func (v *Var) String() string { + var str string + str = fmt.Sprintf("%v %v", v.name, v.node) + return str +} + +func readline() bool { + s, err := fi.ReadString('\n') + if err != nil { + return true + } + line = s + linep = 0 + return false +} + +func getrune() rune { + var c rune + var n int + + if linep >= len(line) { + return 0 + } + c, n = utf8.DecodeRuneInString(line[linep:len(line)]) + linep += n + if c == '\n' { + c = 0 + } + return c +} + +var symmap = make(map[string]*Var) // symbol table + +func lookup(f int) *Var { + var p float64 + var w *Var + + v, ok := symmap[sym] + if ok { + return v + } + if f != 0 { + return nil + } + v = new(Var) + v.name = sym + symmap[sym] = v + + p = 1 + for { + p = fmul(p, pname()) + if p == 0 { + break + } + w = lookup(1) + if w != nil { + v.node = w.node + v.node.vval = fmul(v.node.vval, p) + break + } + } + return v +} + +type Prefix struct { + vval float64 + name string +} + +var prefix = []Prefix{ // prefix table + {1e-24, "yocto"}, + {1e-21, "zepto"}, + {1e-18, "atto"}, + {1e-15, "femto"}, + {1e-12, "pico"}, + {1e-9, "nano"}, + {1e-6, "micro"}, + {1e-6, "μ"}, + {1e-3, "milli"}, + {1e-2, "centi"}, + {1e-1, "deci"}, + {1e1, "deka"}, + {1e2, "hecta"}, + {1e2, "hecto"}, + {1e3, "kilo"}, + {1e6, "mega"}, + {1e6, "meg"}, + {1e9, "giga"}, + {1e12, "tera"}, + {1e15, "peta"}, + {1e18, "exa"}, + {1e21, "zetta"}, + {1e24, "yotta"}, +} + +func pname() float64 { + var i, j, n int + var s string + + /* + * rip off normal prefixs + */ + n = len(sym) + for i = 0; i < len(prefix); i++ { + s = prefix[i].name + j = len(s) + if j < n && sym[0:j] == s { + sym = sym[j:n] + return prefix[i].vval + } + } + + /* + * rip off 's' suffixes + */ + if n > 2 && sym[n-1] == 's' { + sym = sym[0 : n-1] + return 1 + } + + return 0 +} + +// careful multiplication +// exponents (log) are checked before multiply +func fmul(a, b float64) float64 { + var l float64 + + if b <= 0 { + if b == 0 { + return 0 + } + l = math.Log(-b) + } else { + l = math.Log(b) + } + + if a <= 0 { + if a == 0 { + return 0 + } + l += math.Log(-a) + } else { + l += math.Log(a) + } + + if l > Maxe { + Error("overflow in multiply") + return 1 + } + if l < -Maxe { + Error("underflow in multiply") + return 0 + } + return a * b +} + +// careful division +// exponents (log) are checked before divide +func fdiv(a, b float64) float64 { + var l float64 + + if b <= 0 { + if b == 0 { + Errorf("division by zero: %v %v", a, b) + return 1 + } + l = math.Log(-b) + } else { + l = math.Log(b) + } + + if a <= 0 { + if a == 0 { + return 0 + } + l -= math.Log(-a) + } else { + l -= math.Log(a) + } + + if l < -Maxe { + Error("overflow in divide") + return 1 + } + if l > Maxe { + Error("underflow in divide") + return 0 + } + return a / b +} + +func fadd(a, b float64) float64 { + return a + b +} diff --git a/src/cmd/yacc/yacc.go b/src/cmd/yacc/yacc.go new file mode 100644 index 000000000..c91a72123 --- /dev/null +++ b/src/cmd/yacc/yacc.go @@ -0,0 +1,3324 @@ +/* +Derived from Inferno's utils/iyacc/yacc.c +http://code.google.com/p/inferno-os/source/browse/utils/iyacc/yacc.c + +This copyright NOTICE applies to all files in this directory and +subdirectories, unless another copyright notice appears in a given +file or subdirectory. If you take substantial code from this software to use in +other programs, you must somehow include with it an appropriate +copyright notice that includes the copyright notice and the other +notices below. It is fine (and often tidier) to do that in a separate +file such as NOTICE, LICENCE or COPYING. + + Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. + Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) + Portions Copyright © 1997-1999 Vita Nuova Limited + Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) + Portions Copyright © 2004,2006 Bruce Ellis + Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) + Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others + Portions Copyright © 2009 The Go Authors. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +package main + +// yacc +// major difference is lack of stem ("y" variable) +// + +import ( + "bufio" + "bytes" + "flag" + "fmt" + "os" + "strings" +) + +// the following are adjustable +// according to memory size +const ( + ACTSIZE = 30000 + NSTATES = 2000 + TEMPSIZE = 2000 + + SYMINC = 50 // increase for non-term or term + RULEINC = 50 // increase for max rule length prodptr[i] + PRODINC = 100 // increase for productions prodptr + WSETINC = 50 // increase for working sets wsets + STATEINC = 200 // increase for states statemem + + NAMESIZE = 50 + NTYPES = 63 + ISIZE = 400 + + PRIVATE = 0xE000 // unicode private use + + // relationships which must hold: + // TEMPSIZE >= NTERMS + NNONTERM + 1; + // TEMPSIZE >= NSTATES; + // + + NTBASE = 010000 + ERRCODE = 8190 + ACCEPTCODE = 8191 + YYLEXUNK = 3 + TOKSTART = 4 //index of first defined token +) + +// no, left, right, binary assoc. +const ( + NOASC = iota + LASC + RASC + BASC +) + +// flags for state generation +const ( + DONE = iota + MUSTDO + MUSTLOOKAHEAD +) + +// flags for a rule having an action, and being reduced +const ( + ACTFLAG = 1 << (iota + 2) + REDFLAG +) + +// output parser flags +const yyFlag = -1000 + +// parse tokens +const ( + IDENTIFIER = PRIVATE + iota + MARK + TERM + LEFT + RIGHT + BINARY + PREC + LCURLY + IDENTCOLON + NUMBER + START + TYPEDEF + TYPENAME + UNION +) + +const ENDFILE = 0 +const EMPTY = 1 +const WHOKNOWS = 0 +const OK = 1 +const NOMORE = -1000 + +// macros for getting associativity and precedence levels +func ASSOC(i int) int { return i & 3 } + +func PLEVEL(i int) int { return (i >> 4) & 077 } + +func TYPE(i int) int { return (i >> 10) & 077 } + +// macros for setting associativity and precedence levels +func SETASC(i, j int) int { return i | j } + +func SETPLEV(i, j int) int { return i | (j << 4) } + +func SETTYPE(i, j int) int { return i | (j << 10) } + +// I/O descriptors +var finput *bufio.Reader // input file +var stderr *bufio.Writer +var ftable *bufio.Writer // y.go file +var fcode = &bytes.Buffer{} // saved code +var foutput *bufio.Writer // y.output file + +var oflag string // -o [y.go] - y.go file +var vflag string // -v [y.output] - y.output file +var lflag bool // -l - disable line directives +var prefix string // name prefix for identifiers, default yy + +func init() { + flag.StringVar(&oflag, "o", "y.go", "parser output") + flag.StringVar(&prefix, "p", "yy", "name prefix to use in generated code") + flag.StringVar(&vflag, "v", "y.output", "create parsing tables") + flag.BoolVar(&lflag, "l", false, "disable line directives") +} + +var stacksize = 200 + +// communication variables between various I/O routines +var infile string // input file name +var numbval int // value of an input number +var tokname string // input token name, slop for runes and 0 +var tokflag = false + +// structure declarations +type Lkset []int + +type Pitem struct { + prod []int + off int // offset within the production + first int // first term or non-term in item + prodno int // production number for sorting +} + +type Item struct { + pitem Pitem + look Lkset +} + +type Symb struct { + name string + value int +} + +type Wset struct { + pitem Pitem + flag int + ws Lkset +} + +// storage of types +var ntypes int // number of types defined +var typeset [NTYPES]string // pointers to type tags + +// token information + +var ntokens = 0 // number of tokens +var tokset []Symb +var toklev []int // vector with the precedence of the terminals + +// nonterminal information + +var nnonter = -1 // the number of nonterminals +var nontrst []Symb +var start int // start symbol + +// state information + +var nstate = 0 // number of states +var pstate = make([]int, NSTATES+2) // index into statemem to the descriptions of the states +var statemem []Item +var tystate = make([]int, NSTATES) // contains type information about the states +var tstates []int // states generated by terminal gotos +var ntstates []int // states generated by nonterminal gotos +var mstates = make([]int, NSTATES) // chain of overflows of term/nonterm generation lists +var lastred int // number of last reduction of a state +var defact = make([]int, NSTATES) // default actions of states + +// lookahead set information + +var lkst []Lkset +var nolook = 0 // flag to turn off lookahead computations +var tbitset = 0 // size of lookahead sets +var clset Lkset // temporary storage for lookahead computations + +// working set information + +var wsets []Wset +var cwp int + +// storage for action table + +var amem []int // action table storage +var memp int // next free action table position +var indgo = make([]int, NSTATES) // index to the stored goto table + +// temporary vector, indexable by states, terms, or ntokens + +var temp1 = make([]int, TEMPSIZE) // temporary storage, indexed by terms + ntokens or states +var lineno = 1 // current input line number +var fatfl = 1 // if on, error is fatal +var nerrors = 0 // number of errors + +// assigned token type values + +var extval = 0 + +// grammar rule information + +var nprod = 1 // number of productions +var prdptr [][]int // pointers to descriptions of productions +var levprd []int // precedence levels for the productions +var rlines []int // line number for this rule + +// statistics collection variables + +var zzgoent = 0 +var zzgobest = 0 +var zzacent = 0 +var zzexcp = 0 +var zzclose = 0 +var zzrrconf = 0 +var zzsrconf = 0 +var zzstate = 0 + +// optimizer arrays + +var yypgo [][]int +var optst [][]int +var ggreed []int +var pgo []int + +var maxspr int // maximum spread of any entry +var maxoff int // maximum offset into a array +var maxa int + +// storage for information about the nonterminals + +var pres [][][]int // vector of pointers to productions yielding each nonterminal +var pfirst []Lkset +var pempty []int // vector of nonterminals nontrivially deriving e + +// random stuff picked out from between functions + +var indebug = 0 // debugging flag for cpfir +var pidebug = 0 // debugging flag for putitem +var gsdebug = 0 // debugging flag for stagen +var cldebug = 0 // debugging flag for closure +var pkdebug = 0 // debugging flag for apack +var g2debug = 0 // debugging for go2gen +var adb = 0 // debugging for callopt + +type Resrv struct { + name string + value int +} + +var resrv = []Resrv{ + {"binary", BINARY}, + {"left", LEFT}, + {"nonassoc", BINARY}, + {"prec", PREC}, + {"right", RIGHT}, + {"start", START}, + {"term", TERM}, + {"token", TERM}, + {"type", TYPEDEF}, + {"union", UNION}, + {"struct", UNION}, +} + +var zznewstate = 0 + +const EOF = -1 +const UTFmax = 0x3f + +func main() { + + setup() // initialize and read productions + + tbitset = (ntokens + 32) / 32 + cpres() // make table of which productions yield a given nonterminal + cempty() // make a table of which nonterminals can match the empty string + cpfir() // make a table of firsts of nonterminals + + stagen() // generate the states + + yypgo = make([][]int, nnonter+1) + optst = make([][]int, nstate) + output() // write the states and the tables + go2out() + + hideprod() + summary() + + callopt() + + others() + + exit(0) +} + +func setup() { + var j, ty int + + stderr = bufio.NewWriter(os.NewFile(2, "stderr")) + foutput = nil + + flag.Parse() + if flag.NArg() != 1 { + usage() + } + if stacksize < 1 { + // never set so cannot happen + fmt.Fprintf(stderr, "yacc: stack size too small\n") + usage() + } + yaccpar = strings.Replace(yaccpartext, "$$", prefix, -1) + openup() + + defin(0, "$end") + extval = PRIVATE // tokens start in unicode 'private use' + defin(0, "error") + defin(1, "$accept") + defin(0, "$unk") + i := 0 + + t := gettok() + +outer: + for { + switch t { + default: + errorf("syntax error tok=%v", t-PRIVATE) + + case MARK, ENDFILE: + break outer + + case ';': + + case START: + t = gettok() + if t != IDENTIFIER { + errorf("bad %%start construction") + } + start = chfind(1, tokname) + + case TYPEDEF: + t = gettok() + if t != TYPENAME { + errorf("bad syntax in %%type") + } + ty = numbval + for { + t = gettok() + switch t { + case IDENTIFIER: + t = chfind(1, tokname) + if t < NTBASE { + j = TYPE(toklev[t]) + if j != 0 && j != ty { + errorf("type redeclaration of token %s", + tokset[t].name) + } else { + toklev[t] = SETTYPE(toklev[t], ty) + } + } else { + j = nontrst[t-NTBASE].value + if j != 0 && j != ty { + errorf("type redeclaration of nonterminal %v", + nontrst[t-NTBASE].name) + } else { + nontrst[t-NTBASE].value = ty + } + } + continue + + case ',': + continue + } + break + } + continue + + case UNION: + cpyunion() + + case LEFT, BINARY, RIGHT, TERM: + // nonzero means new prec. and assoc. + lev := t - TERM + if lev != 0 { + i++ + } + ty = 0 + + // get identifiers so defined + t = gettok() + + // there is a type defined + if t == TYPENAME { + ty = numbval + t = gettok() + } + for { + switch t { + case ',': + t = gettok() + continue + + case ';': + break + + case IDENTIFIER: + j = chfind(0, tokname) + if j >= NTBASE { + errorf("%v defined earlier as nonterminal", tokname) + } + if lev != 0 { + if ASSOC(toklev[j]) != 0 { + errorf("redeclaration of precedence of %v", tokname) + } + toklev[j] = SETASC(toklev[j], lev) + toklev[j] = SETPLEV(toklev[j], i) + } + if ty != 0 { + if TYPE(toklev[j]) != 0 { + errorf("redeclaration of type of %v", tokname) + } + toklev[j] = SETTYPE(toklev[j], ty) + } + t = gettok() + if t == NUMBER { + tokset[j].value = numbval + t = gettok() + } + + continue + } + break + } + continue + + case LCURLY: + cpycode() + } + t = gettok() + } + + if t == ENDFILE { + errorf("unexpected EOF before %%") + } + + // put out non-literal terminals + for i := TOKSTART; i <= ntokens; i++ { + // non-literals + c := tokset[i].name[0] + if c != ' ' && c != '$' { + fmt.Fprintf(ftable, "const %v = %v\n", tokset[i].name, tokset[i].value) + } + } + + // put out names of token names + ftable.WriteRune('\n') + fmt.Fprintf(ftable, "var %sToknames = []string{\n", prefix) + for i := TOKSTART; i <= ntokens; i++ { + fmt.Fprintf(ftable, "\t\"%v\",\n", tokset[i].name) + } + fmt.Fprintf(ftable, "}\n") + + // put out names of state names + fmt.Fprintf(ftable, "var %sStatenames = []string{", prefix) + // for i:=TOKSTART; i<=ntokens; i++ { + // fmt.Fprintf(ftable, "\t\"%v\",\n", tokset[i].name); + // } + fmt.Fprintf(ftable, "}\n") + + fmt.Fprintf(fcode, "switch %snt {\n", prefix) + + moreprod() + prdptr[0] = []int{NTBASE, start, 1, 0} + + nprod = 1 + curprod := make([]int, RULEINC) + t = gettok() + if t != IDENTCOLON { + errorf("bad syntax on first rule") + } + + if start == 0 { + prdptr[0][1] = chfind(1, tokname) + } + + // read rules + // put into prdptr array in the format + // target + // followed by id's of terminals and non-terminals + // followd by -nprod + + for t != MARK && t != ENDFILE { + mem := 0 + + // process a rule + rlines[nprod] = lineno + if t == '|' { + curprod[mem] = prdptr[nprod-1][0] + mem++ + } else if t == IDENTCOLON { + curprod[mem] = chfind(1, tokname) + if curprod[mem] < NTBASE { + errorf("token illegal on LHS of grammar rule") + } + mem++ + } else { + errorf("illegal rule: missing semicolon or | ?") + } + + // read rule body + t = gettok() + for { + for t == IDENTIFIER { + curprod[mem] = chfind(1, tokname) + if curprod[mem] < NTBASE { + levprd[nprod] = toklev[curprod[mem]] + } + mem++ + if mem >= len(curprod) { + ncurprod := make([]int, mem+RULEINC) + copy(ncurprod, curprod) + curprod = ncurprod + } + t = gettok() + } + if t == PREC { + if gettok() != IDENTIFIER { + errorf("illegal %%prec syntax") + } + j = chfind(2, tokname) + if j >= NTBASE { + errorf("nonterminal " + nontrst[j-NTBASE].name + " illegal after %%prec") + } + levprd[nprod] = toklev[j] + t = gettok() + } + if t != '=' { + break + } + levprd[nprod] |= ACTFLAG + fmt.Fprintf(fcode, "\n\tcase %v:", nprod) + cpyact(curprod, mem) + + // action within rule... + t = gettok() + if t == IDENTIFIER { + // make it a nonterminal + j = chfind(1, fmt.Sprintf("$$%v", nprod)) + + // + // the current rule will become rule number nprod+1 + // enter null production for action + // + prdptr[nprod] = make([]int, 2) + prdptr[nprod][0] = j + prdptr[nprod][1] = -nprod + + // update the production information + nprod++ + moreprod() + levprd[nprod] = levprd[nprod-1] & ^ACTFLAG + levprd[nprod-1] = ACTFLAG + rlines[nprod] = lineno + + // make the action appear in the original rule + curprod[mem] = j + mem++ + if mem >= len(curprod) { + ncurprod := make([]int, mem+RULEINC) + copy(ncurprod, curprod) + curprod = ncurprod + } + } + } + + for t == ';' { + t = gettok() + } + curprod[mem] = -nprod + mem++ + + // check that default action is reasonable + if ntypes != 0 && (levprd[nprod]&ACTFLAG) == 0 && + nontrst[curprod[0]-NTBASE].value != 0 { + // no explicit action, LHS has value + tempty := curprod[1] + if tempty < 0 { + errorf("must return a value, since LHS has a type") + } + if tempty >= NTBASE { + tempty = nontrst[tempty-NTBASE].value + } else { + tempty = TYPE(toklev[tempty]) + } + if tempty != nontrst[curprod[0]-NTBASE].value { + errorf("default action causes potential type clash") + } + fmt.Fprintf(fcode, "\n\tcase %v:", nprod) + fmt.Fprintf(fcode, "\n\t\t%sVAL.%v = %sS[%spt-0].%v", + prefix, typeset[tempty], prefix, prefix, typeset[tempty]) + } + moreprod() + prdptr[nprod] = make([]int, mem) + copy(prdptr[nprod], curprod) + nprod++ + moreprod() + levprd[nprod] = 0 + } + + // + // end of all rules + // dump out the prefix code + // + + fmt.Fprintf(fcode, "\n\t}") + + ftable.WriteRune('\n') + fmt.Fprintf(ftable, "const %sEofCode = 1\n", prefix) + fmt.Fprintf(ftable, "const %sErrCode = 2\n", prefix) + fmt.Fprintf(ftable, "const %sMaxDepth = %v\n", prefix, stacksize) + + // + // copy any postfix code + // + if t == MARK { + if !lflag { + fmt.Fprintf(ftable, "\n//line %v:%v\n", infile, lineno) + } + for { + c := getrune(finput) + if c == EOF { + break + } + ftable.WriteRune(c) + } + } +} + +// +// allocate enough room to hold another production +// +func moreprod() { + n := len(prdptr) + if nprod >= n { + nn := n + PRODINC + aprod := make([][]int, nn) + alevprd := make([]int, nn) + arlines := make([]int, nn) + + copy(aprod, prdptr) + copy(alevprd, levprd) + copy(arlines, rlines) + + prdptr = aprod + levprd = alevprd + rlines = arlines + } +} + +// +// define s to be a terminal if t=0 +// or a nonterminal if t=1 +// +func defin(nt int, s string) int { + val := 0 + if nt != 0 { + nnonter++ + if nnonter >= len(nontrst) { + anontrst := make([]Symb, nnonter+SYMINC) + copy(anontrst, nontrst) + nontrst = anontrst + } + nontrst[nnonter] = Symb{s, 0} + return NTBASE + nnonter + } + + // must be a token + ntokens++ + if ntokens >= len(tokset) { + nn := ntokens + SYMINC + atokset := make([]Symb, nn) + atoklev := make([]int, nn) + + copy(atoklev, toklev) + copy(atokset, tokset) + + tokset = atokset + toklev = atoklev + } + tokset[ntokens].name = s + toklev[ntokens] = 0 + + // establish value for token + // single character literal + if s[0] == ' ' && len(s) == 1+1 { + val = int(s[1]) + } else if s[0] == ' ' && s[1] == '\\' { // escape sequence + if len(s) == 2+1 { + // single character escape sequence + switch s[2] { + case '\'': + val = '\'' + case '"': + val = '"' + case '\\': + val = '\\' + case 'a': + val = '\a' + case 'b': + val = '\b' + case 'n': + val = '\n' + case 'r': + val = '\r' + case 't': + val = '\t' + case 'v': + val = '\v' + default: + errorf("invalid escape %v", s[1:3]) + } + } else if s[2] == 'u' && len(s) == 2+1+4 { // \unnnn sequence + val = 0 + s = s[3:] + for s != "" { + c := int(s[0]) + switch { + case c >= '0' && c <= '9': + c -= '0' + case c >= 'a' && c <= 'f': + c -= 'a' - 10 + case c >= 'A' && c <= 'F': + c -= 'A' - 10 + default: + errorf("illegal \\unnnn construction") + } + val = val*16 + c + s = s[1:] + } + if val == 0 { + errorf("'\\u0000' is illegal") + } + } else { + errorf("unknown escape") + } + } else { + val = extval + extval++ + } + + tokset[ntokens].value = val + return ntokens +} + +var peekline = 0 + +func gettok() int { + var i int + var match, c rune + + tokname = "" + for { + lineno += peekline + peekline = 0 + c = getrune(finput) + for c == ' ' || c == '\n' || c == '\t' || c == '\v' || c == '\r' { + if c == '\n' { + lineno++ + } + c = getrune(finput) + } + + // skip comment -- fix + if c != '/' { + break + } + lineno += skipcom() + } + + switch c { + case EOF: + if tokflag { + fmt.Printf(">>> ENDFILE %v\n", lineno) + } + return ENDFILE + + case '{': + ungetrune(finput, c) + if tokflag { + fmt.Printf(">>> ={ %v\n", lineno) + } + return '=' + + case '<': + // get, and look up, a type name (union member name) + c = getrune(finput) + for c != '>' && c != EOF && c != '\n' { + tokname += string(c) + c = getrune(finput) + } + + if c != '>' { + errorf("unterminated < ... > clause") + } + + for i = 1; i <= ntypes; i++ { + if typeset[i] == tokname { + numbval = i + if tokflag { + fmt.Printf(">>> TYPENAME old <%v> %v\n", tokname, lineno) + } + return TYPENAME + } + } + ntypes++ + numbval = ntypes + typeset[numbval] = tokname + if tokflag { + fmt.Printf(">>> TYPENAME new <%v> %v\n", tokname, lineno) + } + return TYPENAME + + case '"', '\'': + match = c + tokname = " " + for { + c = getrune(finput) + if c == '\n' || c == EOF { + errorf("illegal or missing ' or \"") + } + if c == '\\' { + tokname += string('\\') + c = getrune(finput) + } else if c == match { + if tokflag { + fmt.Printf(">>> IDENTIFIER \"%v\" %v\n", tokname, lineno) + } + return IDENTIFIER + } + tokname += string(c) + } + + case '%': + c = getrune(finput) + switch c { + case '%': + if tokflag { + fmt.Printf(">>> MARK %%%% %v\n", lineno) + } + return MARK + case '=': + if tokflag { + fmt.Printf(">>> PREC %%= %v\n", lineno) + } + return PREC + case '{': + if tokflag { + fmt.Printf(">>> LCURLY %%{ %v\n", lineno) + } + return LCURLY + } + + getword(c) + // find a reserved word + for i := range resrv { + if tokname == resrv[i].name { + if tokflag { + fmt.Printf(">>> %%%v %v %v\n", tokname, + resrv[i].value-PRIVATE, lineno) + } + return resrv[i].value + } + } + errorf("invalid escape, or illegal reserved word: %v", tokname) + + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + numbval = int(c - '0') + for { + c = getrune(finput) + if !isdigit(c) { + break + } + numbval = numbval*10 + int(c-'0') + } + ungetrune(finput, c) + if tokflag { + fmt.Printf(">>> NUMBER %v %v\n", numbval, lineno) + } + return NUMBER + + default: + if isword(c) || c == '.' || c == '$' { + getword(c) + break + } + if tokflag { + fmt.Printf(">>> OPERATOR %v %v\n", string(c), lineno) + } + return int(c) + } + + // look ahead to distinguish IDENTIFIER from IDENTCOLON + c = getrune(finput) + for c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\r' || c == '/' { + if c == '\n' { + peekline++ + } + // look for comments + if c == '/' { + peekline += skipcom() + } + c = getrune(finput) + } + if c == ':' { + if tokflag { + fmt.Printf(">>> IDENTCOLON %v: %v\n", tokname, lineno) + } + return IDENTCOLON + } + + ungetrune(finput, c) + if tokflag { + fmt.Printf(">>> IDENTIFIER %v %v\n", tokname, lineno) + } + return IDENTIFIER +} + +func getword(c rune) { + tokname = "" + for isword(c) || isdigit(c) || c == '_' || c == '.' || c == '$' { + tokname += string(c) + c = getrune(finput) + } + ungetrune(finput, c) +} + +// +// determine the type of a symbol +// +func fdtype(t int) int { + var v int + var s string + + if t >= NTBASE { + v = nontrst[t-NTBASE].value + s = nontrst[t-NTBASE].name + } else { + v = TYPE(toklev[t]) + s = tokset[t].name + } + if v <= 0 { + errorf("must specify type for %v", s) + } + return v +} + +func chfind(t int, s string) int { + if s[0] == ' ' { + t = 0 + } + for i := 0; i <= ntokens; i++ { + if s == tokset[i].name { + return i + } + } + for i := 0; i <= nnonter; i++ { + if s == nontrst[i].name { + return NTBASE + i + } + } + + // cannot find name + if t > 1 { + errorf("%v should have been defined earlier", s) + } + return defin(t, s) +} + +// +// copy the union declaration to the output, and the define file if present +// +func cpyunion() { + + if !lflag { + fmt.Fprintf(ftable, "\n//line %v:%v\n", infile, lineno) + } + fmt.Fprintf(ftable, "type %sSymType struct", prefix) + + level := 0 + +out: + for { + c := getrune(finput) + if c == EOF { + errorf("EOF encountered while processing %%union") + } + ftable.WriteRune(c) + switch c { + case '\n': + lineno++ + case '{': + if level == 0 { + fmt.Fprintf(ftable, "\n\tyys int") + } + level++ + case '}': + level-- + if level == 0 { + break out + } + } + } + fmt.Fprintf(ftable, "\n\n") +} + +// +// saves code between %{ and %} +// +func cpycode() { + lno := lineno + + c := getrune(finput) + if c == '\n' { + c = getrune(finput) + lineno++ + } + if !lflag { + fmt.Fprintf(ftable, "\n//line %v:%v\n", infile, lineno) + } + for c != EOF { + if c == '%' { + c = getrune(finput) + if c == '}' { + return + } + ftable.WriteRune('%') + } + ftable.WriteRune(c) + if c == '\n' { + lineno++ + } + c = getrune(finput) + } + lineno = lno + errorf("eof before %%}") +} + +// +// skip over comments +// skipcom is called after reading a '/' +// +func skipcom() int { + var c rune + + c = getrune(finput) + if c == '/' { + for c != EOF { + if c == '\n' { + return 1 + } + c = getrune(finput) + } + errorf("EOF inside comment") + return 0 + } + if c != '*' { + errorf("illegal comment") + } + + nl := 0 // lines skipped + c = getrune(finput) + +l1: + switch c { + case '*': + c = getrune(finput) + if c == '/' { + break + } + goto l1 + + case '\n': + nl++ + fallthrough + + default: + c = getrune(finput) + goto l1 + } + return nl +} + +func dumpprod(curprod []int, max int) { + fmt.Printf("\n") + for i := 0; i < max; i++ { + p := curprod[i] + if p < 0 { + fmt.Printf("[%v] %v\n", i, p) + } else { + fmt.Printf("[%v] %v\n", i, symnam(p)) + } + } +} + +// +// copy action to the next ; or closing } +// +func cpyact(curprod []int, max int) { + + if !lflag { + fmt.Fprintf(fcode, "\n\t\t//line %v:%v\n\t\t", infile, lineno) + } + + lno := lineno + brac := 0 + +loop: + for { + c := getrune(finput) + + swt: + switch c { + case ';': + if brac == 0 { + fcode.WriteRune(c) + return + } + + case '{': + if brac == 0 { + } + brac++ + + case '$': + s := 1 + tok := -1 + c = getrune(finput) + + // type description + if c == '<' { + ungetrune(finput, c) + if gettok() != TYPENAME { + errorf("bad syntax on $<ident> clause") + } + tok = numbval + c = getrune(finput) + } + if c == '$' { + fmt.Fprintf(fcode, "%sVAL", prefix) + + // put out the proper tag... + if ntypes != 0 { + if tok < 0 { + tok = fdtype(curprod[0]) + } + fmt.Fprintf(fcode, ".%v", typeset[tok]) + } + continue loop + } + if c == '-' { + s = -s + c = getrune(finput) + } + j := 0 + if isdigit(c) { + for isdigit(c) { + j = j*10 + int(c-'0') + c = getrune(finput) + } + ungetrune(finput, c) + j = j * s + if j >= max { + errorf("Illegal use of $%v", j) + } + } else if isword(c) || c == '_' || c == '.' { + // look for $name + ungetrune(finput, c) + if gettok() != IDENTIFIER { + errorf("$ must be followed by an identifier") + } + tokn := chfind(2, tokname) + fnd := -1 + c = getrune(finput) + if c != '@' { + ungetrune(finput, c) + } else if gettok() != NUMBER { + errorf("@ must be followed by number") + } else { + fnd = numbval + } + for j = 1; j < max; j++ { + if tokn == curprod[j] { + fnd-- + if fnd <= 0 { + break + } + } + } + if j >= max { + errorf("$name or $name@number not found") + } + } else { + fcode.WriteRune('$') + if s < 0 { + fcode.WriteRune('-') + } + ungetrune(finput, c) + continue loop + } + fmt.Fprintf(fcode, "%sS[%spt-%v]", prefix, prefix, max-j-1) + + // put out the proper tag + if ntypes != 0 { + if j <= 0 && tok < 0 { + errorf("must specify type of $%v", j) + } + if tok < 0 { + tok = fdtype(curprod[j]) + } + fmt.Fprintf(fcode, ".%v", typeset[tok]) + } + continue loop + + case '}': + brac-- + if brac != 0 { + break + } + fcode.WriteRune(c) + return + + case '/': + nc := getrune(finput) + if nc != '/' && nc != '*' { + ungetrune(finput, nc) + break + } + // a comment + fcode.WriteRune(c) + fcode.WriteRune(nc) + c = getrune(finput) + for c != EOF { + switch { + case c == '\n': + lineno++ + if nc == '/' { // end of // comment + break swt + } + case c == '*' && nc == '*': // end of /* comment? + nnc := getrune(finput) + if nnc == '/' { + fcode.WriteRune('*') + fcode.WriteRune('/') + c = getrune(finput) + break swt + } + ungetrune(finput, nnc) + } + fcode.WriteRune(c) + c = getrune(finput) + } + errorf("EOF inside comment") + + case '\'', '"': + // character string or constant + match := c + fcode.WriteRune(c) + c = getrune(finput) + for c != EOF { + if c == '\\' { + fcode.WriteRune(c) + c = getrune(finput) + if c == '\n' { + lineno++ + } + } else if c == match { + break swt + } + if c == '\n' { + errorf("newline in string or char const") + } + fcode.WriteRune(c) + c = getrune(finput) + } + errorf("EOF in string or character constant") + + case EOF: + lineno = lno + errorf("action does not terminate") + + case '\n': + fmt.Fprint(fcode, "\n\t") + lineno++ + continue loop + } + + fcode.WriteRune(c) + } +} + +func openup() { + infile = flag.Arg(0) + finput = open(infile) + if finput == nil { + errorf("cannot open %v", infile) + } + + foutput = nil + if vflag != "" { + foutput = create(vflag) + if foutput == nil { + errorf("can't create file %v", vflag) + } + } + + ftable = nil + if oflag == "" { + oflag = "y.go" + } + ftable = create(oflag) + if ftable == nil { + errorf("can't create file %v", oflag) + } + +} + +// +// return a pointer to the name of symbol i +// +func symnam(i int) string { + var s string + + if i >= NTBASE { + s = nontrst[i-NTBASE].name + } else { + s = tokset[i].name + } + if s[0] == ' ' { + s = s[1:] + } + return s +} + +// +// set elements 0 through n-1 to c +// +func aryfil(v []int, n, c int) { + for i := 0; i < n; i++ { + v[i] = c + } +} + +// +// compute an array with the beginnings of productions yielding given nonterminals +// The array pres points to these lists +// the array pyield has the lists: the total size is only NPROD+1 +// +func cpres() { + pres = make([][][]int, nnonter+1) + curres := make([][]int, nprod) + + if false { + for j := 0; j <= nnonter; j++ { + fmt.Printf("nnonter[%v] = %v\n", j, nontrst[j].name) + } + for j := 0; j < nprod; j++ { + fmt.Printf("prdptr[%v][0] = %v+NTBASE\n", j, prdptr[j][0]-NTBASE) + } + } + + fatfl = 0 // make undefined symbols nonfatal + for i := 0; i <= nnonter; i++ { + n := 0 + c := i + NTBASE + for j := 0; j < nprod; j++ { + if prdptr[j][0] == c { + curres[n] = prdptr[j][1:] + n++ + } + } + if n == 0 { + errorf("nonterminal %v not defined", nontrst[i].name) + continue + } + pres[i] = make([][]int, n) + copy(pres[i], curres) + } + fatfl = 1 + if nerrors != 0 { + summary() + exit(1) + } +} + +func dumppres() { + for i := 0; i <= nnonter; i++ { + fmt.Printf("nonterm %d\n", i) + curres := pres[i] + for j := 0; j < len(curres); j++ { + fmt.Printf("\tproduction %d:", j) + prd := curres[j] + for k := 0; k < len(prd); k++ { + fmt.Printf(" %d", prd[k]) + } + fmt.Print("\n") + } + } +} + +// +// mark nonterminals which derive the empty string +// also, look for nonterminals which don't derive any token strings +// +func cempty() { + var i, p, np int + var prd []int + + pempty = make([]int, nnonter+1) + + // first, use the array pempty to detect productions that can never be reduced + // set pempty to WHONOWS + aryfil(pempty, nnonter+1, WHOKNOWS) + + // now, look at productions, marking nonterminals which derive something +more: + for { + for i = 0; i < nprod; i++ { + prd = prdptr[i] + if pempty[prd[0]-NTBASE] != 0 { + continue + } + np = len(prd) - 1 + for p = 1; p < np; p++ { + if prd[p] >= NTBASE && pempty[prd[p]-NTBASE] == WHOKNOWS { + break + } + } + // production can be derived + if p == np { + pempty[prd[0]-NTBASE] = OK + continue more + } + } + break + } + + // now, look at the nonterminals, to see if they are all OK + for i = 0; i <= nnonter; i++ { + // the added production rises or falls as the start symbol ... + if i == 0 { + continue + } + if pempty[i] != OK { + fatfl = 0 + errorf("nonterminal " + nontrst[i].name + " never derives any token string") + } + } + + if nerrors != 0 { + summary() + exit(1) + } + + // now, compute the pempty array, to see which nonterminals derive the empty string + // set pempty to WHOKNOWS + aryfil(pempty, nnonter+1, WHOKNOWS) + + // loop as long as we keep finding empty nonterminals + +again: + for { + next: + for i = 1; i < nprod; i++ { + // not known to be empty + prd = prdptr[i] + if pempty[prd[0]-NTBASE] != WHOKNOWS { + continue + } + np = len(prd) - 1 + for p = 1; p < np; p++ { + if prd[p] < NTBASE || pempty[prd[p]-NTBASE] != EMPTY { + continue next + } + } + + // we have a nontrivially empty nonterminal + pempty[prd[0]-NTBASE] = EMPTY + + // got one ... try for another + continue again + } + return + } +} + +func dumpempty() { + for i := 0; i <= nnonter; i++ { + if pempty[i] == EMPTY { + fmt.Printf("non-term %d %s matches empty\n", i, symnam(i+NTBASE)) + } + } +} + +// +// compute an array with the first of nonterminals +// +func cpfir() { + var s, n, p, np, ch, i int + var curres [][]int + var prd []int + + wsets = make([]Wset, nnonter+WSETINC) + pfirst = make([]Lkset, nnonter+1) + for i = 0; i <= nnonter; i++ { + wsets[i].ws = mkset() + pfirst[i] = mkset() + curres = pres[i] + n = len(curres) + + // initially fill the sets + for s = 0; s < n; s++ { + prd = curres[s] + np = len(prd) - 1 + for p = 0; p < np; p++ { + ch = prd[p] + if ch < NTBASE { + setbit(pfirst[i], ch) + break + } + if pempty[ch-NTBASE] == 0 { + break + } + } + } + } + + // now, reflect transitivity + changes := 1 + for changes != 0 { + changes = 0 + for i = 0; i <= nnonter; i++ { + curres = pres[i] + n = len(curres) + for s = 0; s < n; s++ { + prd = curres[s] + np = len(prd) - 1 + for p = 0; p < np; p++ { + ch = prd[p] - NTBASE + if ch < 0 { + break + } + changes |= setunion(pfirst[i], pfirst[ch]) + if pempty[ch] == 0 { + break + } + } + } + } + } + + if indebug == 0 { + return + } + if foutput != nil { + for i = 0; i <= nnonter; i++ { + fmt.Fprintf(foutput, "\n%v: %v %v\n", + nontrst[i].name, pfirst[i], pempty[i]) + } + } +} + +// +// generate the states +// +func stagen() { + // initialize + nstate = 0 + tstates = make([]int, ntokens+1) // states generated by terminal gotos + ntstates = make([]int, nnonter+1) // states generated by nonterminal gotos + amem = make([]int, ACTSIZE) + memp = 0 + + clset = mkset() + pstate[0] = 0 + pstate[1] = 0 + aryfil(clset, tbitset, 0) + putitem(Pitem{prdptr[0], 0, 0, 0}, clset) + tystate[0] = MUSTDO + nstate = 1 + pstate[2] = pstate[1] + + // + // now, the main state generation loop + // first pass generates all of the states + // later passes fix up lookahead + // could be sped up a lot by remembering + // results of the first pass rather than recomputing + // + first := 1 + for more := 1; more != 0; first = 0 { + more = 0 + for i := 0; i < nstate; i++ { + if tystate[i] != MUSTDO { + continue + } + + tystate[i] = DONE + aryfil(temp1, nnonter+1, 0) + + // take state i, close it, and do gotos + closure(i) + + // generate goto's + for p := 0; p < cwp; p++ { + pi := wsets[p] + if pi.flag != 0 { + continue + } + wsets[p].flag = 1 + c := pi.pitem.first + if c <= 1 { + if pstate[i+1]-pstate[i] <= p { + tystate[i] = MUSTLOOKAHEAD + } + continue + } + + // do a goto on c + putitem(wsets[p].pitem, wsets[p].ws) + for q := p + 1; q < cwp; q++ { + // this item contributes to the goto + if c == wsets[q].pitem.first { + putitem(wsets[q].pitem, wsets[q].ws) + wsets[q].flag = 1 + } + } + + if c < NTBASE { + state(c) // register new state + } else { + temp1[c-NTBASE] = state(c) + } + } + + if gsdebug != 0 && foutput != nil { + fmt.Fprintf(foutput, "%v: ", i) + for j := 0; j <= nnonter; j++ { + if temp1[j] != 0 { + fmt.Fprintf(foutput, "%v %v,", nontrst[j].name, temp1[j]) + } + } + fmt.Fprintf(foutput, "\n") + } + + if first != 0 { + indgo[i] = apack(temp1[1:], nnonter-1) - 1 + } + + more++ + } + } +} + +// +// generate the closure of state i +// +func closure(i int) { + zzclose++ + + // first, copy kernel of state i to wsets + cwp = 0 + q := pstate[i+1] + for p := pstate[i]; p < q; p++ { + wsets[cwp].pitem = statemem[p].pitem + wsets[cwp].flag = 1 // this item must get closed + copy(wsets[cwp].ws, statemem[p].look) + cwp++ + } + + // now, go through the loop, closing each item + work := 1 + for work != 0 { + work = 0 + for u := 0; u < cwp; u++ { + if wsets[u].flag == 0 { + continue + } + + // dot is before c + c := wsets[u].pitem.first + if c < NTBASE { + wsets[u].flag = 0 + // only interesting case is where . is before nonterminal + continue + } + + // compute the lookahead + aryfil(clset, tbitset, 0) + + // find items involving c + for v := u; v < cwp; v++ { + if wsets[v].flag != 1 || wsets[v].pitem.first != c { + continue + } + pi := wsets[v].pitem.prod + ipi := wsets[v].pitem.off + 1 + + wsets[v].flag = 0 + if nolook != 0 { + continue + } + + ch := pi[ipi] + ipi++ + for ch > 0 { + // terminal symbol + if ch < NTBASE { + setbit(clset, ch) + break + } + + // nonterminal symbol + setunion(clset, pfirst[ch-NTBASE]) + if pempty[ch-NTBASE] == 0 { + break + } + ch = pi[ipi] + ipi++ + } + if ch <= 0 { + setunion(clset, wsets[v].ws) + } + } + + // + // now loop over productions derived from c + // + curres := pres[c-NTBASE] + n := len(curres) + + nexts: + // initially fill the sets + for s := 0; s < n; s++ { + prd := curres[s] + + // + // put these items into the closure + // is the item there + // + for v := 0; v < cwp; v++ { + // yes, it is there + if wsets[v].pitem.off == 0 && + aryeq(wsets[v].pitem.prod, prd) != 0 { + if nolook == 0 && + setunion(wsets[v].ws, clset) != 0 { + wsets[v].flag = 1 + work = 1 + } + continue nexts + } + } + + // not there; make a new entry + if cwp >= len(wsets) { + awsets := make([]Wset, cwp+WSETINC) + copy(awsets, wsets) + wsets = awsets + } + wsets[cwp].pitem = Pitem{prd, 0, prd[0], -prd[len(prd)-1]} + wsets[cwp].flag = 1 + wsets[cwp].ws = mkset() + if nolook == 0 { + work = 1 + copy(wsets[cwp].ws, clset) + } + cwp++ + } + } + } + + // have computed closure; flags are reset; return + if cldebug != 0 && foutput != nil { + fmt.Fprintf(foutput, "\nState %v, nolook = %v\n", i, nolook) + for u := 0; u < cwp; u++ { + if wsets[u].flag != 0 { + fmt.Fprintf(foutput, "flag set\n") + } + wsets[u].flag = 0 + fmt.Fprintf(foutput, "\t%v", writem(wsets[u].pitem)) + prlook(wsets[u].ws) + fmt.Fprintf(foutput, "\n") + } + } +} + +// +// sorts last state,and sees if it equals earlier ones. returns state number +// +func state(c int) int { + zzstate++ + p1 := pstate[nstate] + p2 := pstate[nstate+1] + if p1 == p2 { + return 0 // null state + } + + // sort the items + var k, l int + for k = p1 + 1; k < p2; k++ { // make k the biggest + for l = k; l > p1; l-- { + if statemem[l].pitem.prodno < statemem[l-1].pitem.prodno || + statemem[l].pitem.prodno == statemem[l-1].pitem.prodno && + statemem[l].pitem.off < statemem[l-1].pitem.off { + s := statemem[l] + statemem[l] = statemem[l-1] + statemem[l-1] = s + } else { + break + } + } + } + + size1 := p2 - p1 // size of state + + var i int + if c >= NTBASE { + i = ntstates[c-NTBASE] + } else { + i = tstates[c] + } + +look: + for ; i != 0; i = mstates[i] { + // get ith state + q1 := pstate[i] + q2 := pstate[i+1] + size2 := q2 - q1 + if size1 != size2 { + continue + } + k = p1 + for l = q1; l < q2; l++ { + if aryeq(statemem[l].pitem.prod, statemem[k].pitem.prod) == 0 || + statemem[l].pitem.off != statemem[k].pitem.off { + continue look + } + k++ + } + + // found it + pstate[nstate+1] = pstate[nstate] // delete last state + + // fix up lookaheads + if nolook != 0 { + return i + } + k = p1 + for l = q1; l < q2; l++ { + if setunion(statemem[l].look, statemem[k].look) != 0 { + tystate[i] = MUSTDO + } + k++ + } + return i + } + + // state is new + zznewstate++ + if nolook != 0 { + errorf("yacc state/nolook error") + } + pstate[nstate+2] = p2 + if nstate+1 >= NSTATES { + errorf("too many states") + } + if c >= NTBASE { + mstates[nstate] = ntstates[c-NTBASE] + ntstates[c-NTBASE] = nstate + } else { + mstates[nstate] = tstates[c] + tstates[c] = nstate + } + tystate[nstate] = MUSTDO + nstate++ + return nstate - 1 +} + +func putitem(p Pitem, set Lkset) { + p.off++ + p.first = p.prod[p.off] + + if pidebug != 0 && foutput != nil { + fmt.Fprintf(foutput, "putitem(%v), state %v\n", writem(p), nstate) + } + j := pstate[nstate+1] + if j >= len(statemem) { + asm := make([]Item, j+STATEINC) + copy(asm, statemem) + statemem = asm + } + statemem[j].pitem = p + if nolook == 0 { + s := mkset() + copy(s, set) + statemem[j].look = s + } + j++ + pstate[nstate+1] = j +} + +// +// creates output string for item pointed to by pp +// +func writem(pp Pitem) string { + var i int + + p := pp.prod + q := chcopy(nontrst[prdptr[pp.prodno][0]-NTBASE].name) + ": " + npi := pp.off + + pi := aryeq(p, prdptr[pp.prodno]) + + for { + c := ' ' + if pi == npi { + c = '.' + } + q += string(c) + + i = p[pi] + pi++ + if i <= 0 { + break + } + q += chcopy(symnam(i)) + } + + // an item calling for a reduction + i = p[npi] + if i < 0 { + q += fmt.Sprintf(" (%v)", -i) + } + + return q +} + +// +// pack state i from temp1 into amem +// +func apack(p []int, n int) int { + // + // we don't need to worry about checking because + // we will only look at entries known to be there... + // eliminate leading and trailing 0's + // + off := 0 + pp := 0 + for ; pp <= n && p[pp] == 0; pp++ { + off-- + } + + // no actions + if pp > n { + return 0 + } + for ; n > pp && p[n] == 0; n-- { + } + p = p[pp : n+1] + + // now, find a place for the elements from p to q, inclusive + r := len(amem) - len(p) + +nextk: + for rr := 0; rr <= r; rr++ { + qq := rr + for pp = 0; pp < len(p); pp++ { + if p[pp] != 0 { + if p[pp] != amem[qq] && amem[qq] != 0 { + continue nextk + } + } + qq++ + } + + // we have found an acceptable k + if pkdebug != 0 && foutput != nil { + fmt.Fprintf(foutput, "off = %v, k = %v\n", off+rr, rr) + } + qq = rr + for pp = 0; pp < len(p); pp++ { + if p[pp] != 0 { + if qq > memp { + memp = qq + } + amem[qq] = p[pp] + } + qq++ + } + if pkdebug != 0 && foutput != nil { + for pp = 0; pp <= memp; pp += 10 { + fmt.Fprintf(foutput, "\n") + for qq = pp; qq <= pp+9; qq++ { + fmt.Fprintf(foutput, "%v ", amem[qq]) + } + fmt.Fprintf(foutput, "\n") + } + } + return off + rr + } + errorf("no space in action table") + return 0 +} + +// +// print the output for the states +// +func output() { + var c, u, v int + + fmt.Fprintf(ftable, "\n//line yacctab:1\n") + fmt.Fprintf(ftable, "var %sExca = []int{\n", prefix) + + noset := mkset() + + // output the stuff for state i + for i := 0; i < nstate; i++ { + nolook = 0 + if tystate[i] != MUSTLOOKAHEAD { + nolook = 1 + } + closure(i) + + // output actions + nolook = 1 + aryfil(temp1, ntokens+nnonter+1, 0) + for u = 0; u < cwp; u++ { + c = wsets[u].pitem.first + if c > 1 && c < NTBASE && temp1[c] == 0 { + for v = u; v < cwp; v++ { + if c == wsets[v].pitem.first { + putitem(wsets[v].pitem, noset) + } + } + temp1[c] = state(c) + } else if c > NTBASE { + c -= NTBASE + if temp1[c+ntokens] == 0 { + temp1[c+ntokens] = amem[indgo[i]+c] + } + } + } + if i == 1 { + temp1[1] = ACCEPTCODE + } + + // now, we have the shifts; look at the reductions + lastred = 0 + for u = 0; u < cwp; u++ { + c = wsets[u].pitem.first + + // reduction + if c > 0 { + continue + } + lastred = -c + us := wsets[u].ws + for k := 0; k <= ntokens; k++ { + if bitset(us, k) == 0 { + continue + } + if temp1[k] == 0 { + temp1[k] = c + } else if temp1[k] < 0 { // reduce/reduce conflict + if foutput != nil { + fmt.Fprintf(foutput, + "\n %v: reduce/reduce conflict (red'ns "+ + "%v and %v) on %v", + i, -temp1[k], lastred, symnam(k)) + } + if -temp1[k] > lastred { + temp1[k] = -lastred + } + zzrrconf++ + } else { + // potential shift/reduce conflict + precftn(lastred, k, i) + } + } + } + wract(i) + } + + fmt.Fprintf(ftable, "}\n") + ftable.WriteRune('\n') + fmt.Fprintf(ftable, "const %sNprod = %v\n", prefix, nprod) + fmt.Fprintf(ftable, "const %sPrivate = %v\n", prefix, PRIVATE) + ftable.WriteRune('\n') + fmt.Fprintf(ftable, "var %sTokenNames []string\n", prefix) + fmt.Fprintf(ftable, "var %sStates []string\n", prefix) +} + +// +// decide a shift/reduce conflict by precedence. +// r is a rule number, t a token number +// the conflict is in state s +// temp1[t] is changed to reflect the action +// +func precftn(r, t, s int) { + var action int + + lp := levprd[r] + lt := toklev[t] + if PLEVEL(lt) == 0 || PLEVEL(lp) == 0 { + // conflict + if foutput != nil { + fmt.Fprintf(foutput, + "\n%v: shift/reduce conflict (shift %v(%v), red'n %v(%v)) on %v", + s, temp1[t], PLEVEL(lt), r, PLEVEL(lp), symnam(t)) + } + zzsrconf++ + return + } + if PLEVEL(lt) == PLEVEL(lp) { + action = ASSOC(lt) + } else if PLEVEL(lt) > PLEVEL(lp) { + action = RASC // shift + } else { + action = LASC + } // reduce + switch action { + case BASC: // error action + temp1[t] = ERRCODE + case LASC: // reduce + temp1[t] = -r + } +} + +// +// output state i +// temp1 has the actions, lastred the default +// +func wract(i int) { + var p, p1 int + + // find the best choice for lastred + lastred = 0 + ntimes := 0 + for j := 0; j <= ntokens; j++ { + if temp1[j] >= 0 { + continue + } + if temp1[j]+lastred == 0 { + continue + } + // count the number of appearances of temp1[j] + count := 0 + tred := -temp1[j] + levprd[tred] |= REDFLAG + for p = 0; p <= ntokens; p++ { + if temp1[p]+tred == 0 { + count++ + } + } + if count > ntimes { + lastred = tred + ntimes = count + } + } + + // + // for error recovery, arrange that, if there is a shift on the + // error recovery token, `error', that the default be the error action + // + if temp1[2] > 0 { + lastred = 0 + } + + // clear out entries in temp1 which equal lastred + // count entries in optst table + n := 0 + for p = 0; p <= ntokens; p++ { + p1 = temp1[p] + if p1+lastred == 0 { + temp1[p] = 0 + p1 = 0 + } + if p1 > 0 && p1 != ACCEPTCODE && p1 != ERRCODE { + n++ + } + } + + wrstate(i) + defact[i] = lastred + flag := 0 + os := make([]int, n*2) + n = 0 + for p = 0; p <= ntokens; p++ { + p1 = temp1[p] + if p1 != 0 { + if p1 < 0 { + p1 = -p1 + } else if p1 == ACCEPTCODE { + p1 = -1 + } else if p1 == ERRCODE { + p1 = 0 + } else { + os[n] = p + n++ + os[n] = p1 + n++ + zzacent++ + continue + } + if flag == 0 { + fmt.Fprintf(ftable, "\t-1, %v,\n", i) + } + flag++ + fmt.Fprintf(ftable, "\t%v, %v,\n", p, p1) + zzexcp++ + } + } + if flag != 0 { + defact[i] = -2 + fmt.Fprintf(ftable, "\t-2, %v,\n", lastred) + } + optst[i] = os +} + +// +// writes state i +// +func wrstate(i int) { + var j0, j1, u int + var pp, qq int + + if foutput == nil { + return + } + fmt.Fprintf(foutput, "\nstate %v\n", i) + qq = pstate[i+1] + for pp = pstate[i]; pp < qq; pp++ { + fmt.Fprintf(foutput, "\t%v\n", writem(statemem[pp].pitem)) + } + if tystate[i] == MUSTLOOKAHEAD { + // print out empty productions in closure + for u = pstate[i+1] - pstate[i]; u < cwp; u++ { + if wsets[u].pitem.first < 0 { + fmt.Fprintf(foutput, "\t%v\n", writem(wsets[u].pitem)) + } + } + } + + // check for state equal to another + for j0 = 0; j0 <= ntokens; j0++ { + j1 = temp1[j0] + if j1 != 0 { + fmt.Fprintf(foutput, "\n\t%v ", symnam(j0)) + + // shift, error, or accept + if j1 > 0 { + if j1 == ACCEPTCODE { + fmt.Fprintf(foutput, "accept") + } else if j1 == ERRCODE { + fmt.Fprintf(foutput, "error") + } else { + fmt.Fprintf(foutput, "shift %v", j1) + } + } else { + fmt.Fprintf(foutput, "reduce %v (src line %v)", -j1, rlines[-j1]) + } + } + } + + // output the final production + if lastred != 0 { + fmt.Fprintf(foutput, "\n\t. reduce %v (src line %v)\n\n", + lastred, rlines[lastred]) + } else { + fmt.Fprintf(foutput, "\n\t. error\n\n") + } + + // now, output nonterminal actions + j1 = ntokens + for j0 = 1; j0 <= nnonter; j0++ { + j1++ + if temp1[j1] != 0 { + fmt.Fprintf(foutput, "\t%v goto %v\n", symnam(j0+NTBASE), temp1[j1]) + } + } +} + +// +// output the gotos for the nontermninals +// +func go2out() { + for i := 1; i <= nnonter; i++ { + go2gen(i) + + // find the best one to make default + best := -1 + times := 0 + + // is j the most frequent + for j := 0; j < nstate; j++ { + if tystate[j] == 0 { + continue + } + if tystate[j] == best { + continue + } + + // is tystate[j] the most frequent + count := 0 + cbest := tystate[j] + for k := j; k < nstate; k++ { + if tystate[k] == cbest { + count++ + } + } + if count > times { + best = cbest + times = count + } + } + + // best is now the default entry + zzgobest += times - 1 + n := 0 + for j := 0; j < nstate; j++ { + if tystate[j] != 0 && tystate[j] != best { + n++ + } + } + goent := make([]int, 2*n+1) + n = 0 + for j := 0; j < nstate; j++ { + if tystate[j] != 0 && tystate[j] != best { + goent[n] = j + n++ + goent[n] = tystate[j] + n++ + zzgoent++ + } + } + + // now, the default + if best == -1 { + best = 0 + } + + zzgoent++ + goent[n] = best + yypgo[i] = goent + } +} + +// +// output the gotos for nonterminal c +// +func go2gen(c int) { + var i, cc, p, q int + + // first, find nonterminals with gotos on c + aryfil(temp1, nnonter+1, 0) + temp1[c] = 1 + work := 1 + for work != 0 { + work = 0 + for i = 0; i < nprod; i++ { + // cc is a nonterminal with a goto on c + cc = prdptr[i][1] - NTBASE + if cc >= 0 && temp1[cc] != 0 { + // thus, the left side of production i does too + cc = prdptr[i][0] - NTBASE + if temp1[cc] == 0 { + work = 1 + temp1[cc] = 1 + } + } + } + } + + // now, we have temp1[c] = 1 if a goto on c in closure of cc + if g2debug != 0 && foutput != nil { + fmt.Fprintf(foutput, "%v: gotos on ", nontrst[c].name) + for i = 0; i <= nnonter; i++ { + if temp1[i] != 0 { + fmt.Fprintf(foutput, "%v ", nontrst[i].name) + } + } + fmt.Fprintf(foutput, "\n") + } + + // now, go through and put gotos into tystate + aryfil(tystate, nstate, 0) + for i = 0; i < nstate; i++ { + q = pstate[i+1] + for p = pstate[i]; p < q; p++ { + cc = statemem[p].pitem.first + if cc >= NTBASE { + // goto on c is possible + if temp1[cc-NTBASE] != 0 { + tystate[i] = amem[indgo[i]+c] + break + } + } + } + } +} + +// +// in order to free up the mem and amem arrays for the optimizer, +// and still be able to output yyr1, etc., after the sizes of +// the action array is known, we hide the nonterminals +// derived by productions in levprd. +// +func hideprod() { + nred := 0 + levprd[0] = 0 + for i := 1; i < nprod; i++ { + if (levprd[i] & REDFLAG) == 0 { + if foutput != nil { + fmt.Fprintf(foutput, "Rule not reduced: %v\n", + writem(Pitem{prdptr[i], 0, 0, i})) + } + fmt.Printf("rule %v never reduced\n", writem(Pitem{prdptr[i], 0, 0, i})) + nred++ + } + levprd[i] = prdptr[i][0] - NTBASE + } + if nred != 0 { + fmt.Printf("%v rules never reduced\n", nred) + } +} + +func callopt() { + var j, k, p, q, i int + var v []int + + pgo = make([]int, nnonter+1) + pgo[0] = 0 + maxoff = 0 + maxspr = 0 + for i = 0; i < nstate; i++ { + k = 32000 + j = 0 + v = optst[i] + q = len(v) + for p = 0; p < q; p += 2 { + if v[p] > j { + j = v[p] + } + if v[p] < k { + k = v[p] + } + } + + // nontrivial situation + if k <= j { + // j is now the range + // j -= k; // call scj + if k > maxoff { + maxoff = k + } + } + tystate[i] = q + 2*j + if j > maxspr { + maxspr = j + } + } + + // initialize ggreed table + ggreed = make([]int, nnonter+1) + for i = 1; i <= nnonter; i++ { + ggreed[i] = 1 + j = 0 + + // minimum entry index is always 0 + v = yypgo[i] + q = len(v) - 1 + for p = 0; p < q; p += 2 { + ggreed[i] += 2 + if v[p] > j { + j = v[p] + } + } + ggreed[i] = ggreed[i] + 2*j + if j > maxoff { + maxoff = j + } + } + + // now, prepare to put the shift actions into the amem array + for i = 0; i < ACTSIZE; i++ { + amem[i] = 0 + } + maxa = 0 + for i = 0; i < nstate; i++ { + if tystate[i] == 0 && adb > 1 { + fmt.Fprintf(ftable, "State %v: null\n", i) + } + indgo[i] = yyFlag + } + + i = nxti() + for i != NOMORE { + if i >= 0 { + stin(i) + } else { + gin(-i) + } + i = nxti() + } + + // print amem array + if adb > 2 { + for p = 0; p <= maxa; p += 10 { + fmt.Fprintf(ftable, "%v ", p) + for i = 0; i < 10; i++ { + fmt.Fprintf(ftable, "%v ", amem[p+i]) + } + ftable.WriteRune('\n') + } + } + + aoutput() + osummary() +} + +// +// finds the next i +// +func nxti() int { + max := 0 + maxi := 0 + for i := 1; i <= nnonter; i++ { + if ggreed[i] >= max { + max = ggreed[i] + maxi = -i + } + } + for i := 0; i < nstate; i++ { + if tystate[i] >= max { + max = tystate[i] + maxi = i + } + } + if max == 0 { + return NOMORE + } + return maxi +} + +func gin(i int) { + var s int + + // enter gotos on nonterminal i into array amem + ggreed[i] = 0 + + q := yypgo[i] + nq := len(q) - 1 + + // now, find amem place for it +nextgp: + for p := 0; p < ACTSIZE; p++ { + if amem[p] != 0 { + continue + } + for r := 0; r < nq; r += 2 { + s = p + q[r] + 1 + if s > maxa { + maxa = s + if maxa >= ACTSIZE { + errorf("a array overflow") + } + } + if amem[s] != 0 { + continue nextgp + } + } + + // we have found amem spot + amem[p] = q[nq] + if p > maxa { + maxa = p + } + for r := 0; r < nq; r += 2 { + s = p + q[r] + 1 + amem[s] = q[r+1] + } + pgo[i] = p + if adb > 1 { + fmt.Fprintf(ftable, "Nonterminal %v, entry at %v\n", i, pgo[i]) + } + return + } + errorf("cannot place goto %v\n", i) +} + +func stin(i int) { + var s int + + tystate[i] = 0 + + // enter state i into the amem array + q := optst[i] + nq := len(q) + +nextn: + // find an acceptable place + for n := -maxoff; n < ACTSIZE; n++ { + flag := 0 + for r := 0; r < nq; r += 2 { + s = q[r] + n + if s < 0 || s > ACTSIZE { + continue nextn + } + if amem[s] == 0 { + flag++ + } else if amem[s] != q[r+1] { + continue nextn + } + } + + // check the position equals another only if the states are identical + for j := 0; j < nstate; j++ { + if indgo[j] == n { + + // we have some disagreement + if flag != 0 { + continue nextn + } + if nq == len(optst[j]) { + + // states are equal + indgo[i] = n + if adb > 1 { + fmt.Fprintf(ftable, "State %v: entry at"+ + "%v equals state %v\n", + i, n, j) + } + return + } + + // we have some disagreement + continue nextn + } + } + + for r := 0; r < nq; r += 2 { + s = q[r] + n + if s > maxa { + maxa = s + } + if amem[s] != 0 && amem[s] != q[r+1] { + errorf("clobber of a array, pos'n %v, by %v", s, q[r+1]) + } + amem[s] = q[r+1] + } + indgo[i] = n + if adb > 1 { + fmt.Fprintf(ftable, "State %v: entry at %v\n", i, indgo[i]) + } + return + } + errorf("Error; failure to place state %v", i) +} + +// +// this version is for limbo +// write out the optimized parser +// +func aoutput() { + ftable.WriteRune('\n') + fmt.Fprintf(ftable, "const %sLast = %v\n\n", prefix, maxa+1) + arout("Act", amem, maxa+1) + arout("Pact", indgo, nstate) + arout("Pgo", pgo, nnonter+1) +} + +// +// put out other arrays, copy the parsers +// +func others() { + var i, j int + + arout("R1", levprd, nprod) + aryfil(temp1, nprod, 0) + + // + //yyr2 is the number of rules for each production + // + for i = 1; i < nprod; i++ { + temp1[i] = len(prdptr[i]) - 2 + } + arout("R2", temp1, nprod) + + aryfil(temp1, nstate, -1000) + for i = 0; i <= ntokens; i++ { + for j := tstates[i]; j != 0; j = mstates[j] { + temp1[j] = i + } + } + for i = 0; i <= nnonter; i++ { + for j = ntstates[i]; j != 0; j = mstates[j] { + temp1[j] = -i + } + } + arout("Chk", temp1, nstate) + arout("Def", defact, nstate) + + // put out token translation tables + // table 1 has 0-256 + aryfil(temp1, 256, 0) + c := 0 + for i = 1; i <= ntokens; i++ { + j = tokset[i].value + if j >= 0 && j < 256 { + if temp1[j] != 0 { + fmt.Print("yacc bug -- cant have 2 different Ts with same value\n") + fmt.Printf(" %s and %s\n", tokset[i].name, tokset[temp1[j]].name) + nerrors++ + } + temp1[j] = i + if j > c { + c = j + } + } + } + for i = 0; i <= c; i++ { + if temp1[i] == 0 { + temp1[i] = YYLEXUNK + } + } + arout("Tok1", temp1, c+1) + + // table 2 has PRIVATE-PRIVATE+256 + aryfil(temp1, 256, 0) + c = 0 + for i = 1; i <= ntokens; i++ { + j = tokset[i].value - PRIVATE + if j >= 0 && j < 256 { + if temp1[j] != 0 { + fmt.Print("yacc bug -- cant have 2 different Ts with same value\n") + fmt.Printf(" %s and %s\n", tokset[i].name, tokset[temp1[j]].name) + nerrors++ + } + temp1[j] = i + if j > c { + c = j + } + } + } + arout("Tok2", temp1, c+1) + + // table 3 has everything else + fmt.Fprintf(ftable, "var %sTok3 = []int{\n\t", prefix) + c = 0 + for i = 1; i <= ntokens; i++ { + j = tokset[i].value + if j >= 0 && j < 256 { + continue + } + if j >= PRIVATE && j < 256+PRIVATE { + continue + } + + if c%5 != 0 { + ftable.WriteRune(' ') + } + fmt.Fprintf(ftable, "%d, %d,", j, i) + c++ + if c%5 == 0 { + fmt.Fprint(ftable, "\n\t") + } + } + if c%5 != 0 { + ftable.WriteRune(' ') + } + fmt.Fprintf(ftable, "%d,\n}\n", 0) + + // copy parser text + ch := getrune(finput) + for ch != EOF { + ftable.WriteRune(ch) + ch = getrune(finput) + } + + // copy yaccpar + fmt.Fprintf(ftable, "\n//line yaccpar:1\n") + + parts := strings.SplitN(yaccpar, prefix+"run()", 2) + fmt.Fprintf(ftable, "%v", parts[0]) + ftable.Write(fcode.Bytes()) + fmt.Fprintf(ftable, "%v", parts[1]) +} + +func arout(s string, v []int, n int) { + s = prefix + s + fmt.Fprintf(ftable, "var %v = []int{\n", s) + for i := 0; i < n; i++ { + if i%10 == 0 { + fmt.Fprintf(ftable, "\n\t") + } else { + ftable.WriteRune(' ') + } + fmt.Fprintf(ftable, "%d,", v[i]) + } + fmt.Fprintf(ftable, "\n}\n") +} + +// +// output the summary on y.output +// +func summary() { + if foutput != nil { + fmt.Fprintf(foutput, "\n%v terminals, %v nonterminals\n", ntokens, nnonter+1) + fmt.Fprintf(foutput, "%v grammar rules, %v/%v states\n", nprod, nstate, NSTATES) + fmt.Fprintf(foutput, "%v shift/reduce, %v reduce/reduce conflicts reported\n", zzsrconf, zzrrconf) + fmt.Fprintf(foutput, "%v working sets used\n", len(wsets)) + fmt.Fprintf(foutput, "memory: parser %v/%v\n", memp, ACTSIZE) + fmt.Fprintf(foutput, "%v extra closures\n", zzclose-2*nstate) + fmt.Fprintf(foutput, "%v shift entries, %v exceptions\n", zzacent, zzexcp) + fmt.Fprintf(foutput, "%v goto entries\n", zzgoent) + fmt.Fprintf(foutput, "%v entries saved by goto default\n", zzgobest) + } + if zzsrconf != 0 || zzrrconf != 0 { + fmt.Printf("\nconflicts: ") + if zzsrconf != 0 { + fmt.Printf("%v shift/reduce", zzsrconf) + } + if zzsrconf != 0 && zzrrconf != 0 { + fmt.Printf(", ") + } + if zzrrconf != 0 { + fmt.Printf("%v reduce/reduce", zzrrconf) + } + fmt.Printf("\n") + } +} + +// +// write optimizer summary +// +func osummary() { + if foutput == nil { + return + } + i := 0 + for p := maxa; p >= 0; p-- { + if amem[p] == 0 { + i++ + } + } + + fmt.Fprintf(foutput, "Optimizer space used: output %v/%v\n", maxa+1, ACTSIZE) + fmt.Fprintf(foutput, "%v table entries, %v zero\n", maxa+1, i) + fmt.Fprintf(foutput, "maximum spread: %v, maximum offset: %v\n", maxspr, maxoff) +} + +// +// copies and protects "'s in q +// +func chcopy(q string) string { + s := "" + i := 0 + j := 0 + for i = 0; i < len(q); i++ { + if q[i] == '"' { + s += q[j:i] + "\\" + j = i + } + } + return s + q[j:i] +} + +func usage() { + fmt.Fprintf(stderr, "usage: yacc [-o output] [-v parsetable] input\n") + exit(1) +} + +func bitset(set Lkset, bit int) int { return set[bit>>5] & (1 << uint(bit&31)) } + +func setbit(set Lkset, bit int) { set[bit>>5] |= (1 << uint(bit&31)) } + +func mkset() Lkset { return make([]int, tbitset) } + +// +// set a to the union of a and b +// return 1 if b is not a subset of a, 0 otherwise +// +func setunion(a, b []int) int { + sub := 0 + for i := 0; i < tbitset; i++ { + x := a[i] + y := x | b[i] + a[i] = y + if y != x { + sub = 1 + } + } + return sub +} + +func prlook(p Lkset) { + if p == nil { + fmt.Fprintf(foutput, "\tNULL") + return + } + fmt.Fprintf(foutput, " { ") + for j := 0; j <= ntokens; j++ { + if bitset(p, j) != 0 { + fmt.Fprintf(foutput, "%v ", symnam(j)) + } + } + fmt.Fprintf(foutput, "}") +} + +// +// utility routines +// +var peekrune rune + +func isdigit(c rune) bool { return c >= '0' && c <= '9' } + +func isword(c rune) bool { + return c >= 0xa0 || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') +} + +func mktemp(t string) string { return t } + +// +// return 1 if 2 arrays are equal +// return 0 if not equal +// +func aryeq(a []int, b []int) int { + n := len(a) + if len(b) != n { + return 0 + } + for ll := 0; ll < n; ll++ { + if a[ll] != b[ll] { + return 0 + } + } + return 1 +} + +func putrune(f *bufio.Writer, c int) { + s := string(c) + for i := 0; i < len(s); i++ { + f.WriteByte(s[i]) + } +} + +func getrune(f *bufio.Reader) rune { + var r rune + + if peekrune != 0 { + if peekrune == EOF { + return EOF + } + r = peekrune + peekrune = 0 + return r + } + + c, n, err := f.ReadRune() + if n == 0 { + return EOF + } + if err != nil { + errorf("read error: %v", err) + } + //fmt.Printf("rune = %v n=%v\n", string(c), n); + return c +} + +func ungetrune(f *bufio.Reader, c rune) { + if f != finput { + panic("ungetc - not finput") + } + if peekrune != 0 { + panic("ungetc - 2nd unget") + } + peekrune = c +} + +func write(f *bufio.Writer, b []byte, n int) int { + panic("write") + return 0 +} + +func open(s string) *bufio.Reader { + fi, err := os.Open(s) + if err != nil { + errorf("error opening %v: %v", s, err) + } + //fmt.Printf("open %v\n", s); + return bufio.NewReader(fi) +} + +func create(s string) *bufio.Writer { + fo, err := os.Create(s) + if err != nil { + errorf("error creating %v: %v", s, err) + } + //fmt.Printf("create %v mode %v\n", s); + return bufio.NewWriter(fo) +} + +// +// write out error comment +// +func errorf(s string, v ...interface{}) { + nerrors++ + fmt.Fprintf(stderr, s, v...) + fmt.Fprintf(stderr, ": %v:%v\n", infile, lineno) + if fatfl != 0 { + summary() + exit(1) + } +} + +func exit(status int) { + if ftable != nil { + ftable.Flush() + ftable = nil + } + if foutput != nil { + foutput.Flush() + foutput = nil + } + if stderr != nil { + stderr.Flush() + stderr = nil + } + os.Exit(status) +} + +var yaccpar string // will be processed version of yaccpartext: s/$$/prefix/g +var yaccpartext = ` +/* parser for yacc output */ + +var $$Debug = 0 + +type $$Lexer interface { + Lex(lval *$$SymType) int + Error(s string) +} + +const $$Flag = -1000 + +func $$Tokname(c int) string { + if c > 0 && c <= len($$Toknames) { + if $$Toknames[c-1] != "" { + return $$Toknames[c-1] + } + } + return fmt.Sprintf("tok-%v", c) +} + +func $$Statname(s int) string { + if s >= 0 && s < len($$Statenames) { + if $$Statenames[s] != "" { + return $$Statenames[s] + } + } + return fmt.Sprintf("state-%v", s) +} + +func $$lex1(lex $$Lexer, lval *$$SymType) int { + c := 0 + char := lex.Lex(lval) + if char <= 0 { + c = $$Tok1[0] + goto out + } + if char < len($$Tok1) { + c = $$Tok1[char] + goto out + } + if char >= $$Private { + if char < $$Private+len($$Tok2) { + c = $$Tok2[char-$$Private] + goto out + } + } + for i := 0; i < len($$Tok3); i += 2 { + c = $$Tok3[i+0] + if c == char { + c = $$Tok3[i+1] + goto out + } + } + +out: + if c == 0 { + c = $$Tok2[1] /* unknown char */ + } + if $$Debug >= 3 { + fmt.Printf("lex %U %s\n", uint(char), $$Tokname(c)) + } + return c +} + +func $$Parse($$lex $$Lexer) int { + var $$n int + var $$lval $$SymType + var $$VAL $$SymType + $$S := make([]$$SymType, $$MaxDepth) + + Nerrs := 0 /* number of errors */ + Errflag := 0 /* error recovery flag */ + $$state := 0 + $$char := -1 + $$p := -1 + goto $$stack + +ret0: + return 0 + +ret1: + return 1 + +$$stack: + /* put a state and value onto the stack */ + if $$Debug >= 4 { + fmt.Printf("char %v in %v\n", $$Tokname($$char), $$Statname($$state)) + } + + $$p++ + if $$p >= len($$S) { + nyys := make([]$$SymType, len($$S)*2) + copy(nyys, $$S) + $$S = nyys + } + $$S[$$p] = $$VAL + $$S[$$p].yys = $$state + +$$newstate: + $$n = $$Pact[$$state] + if $$n <= $$Flag { + goto $$default /* simple state */ + } + if $$char < 0 { + $$char = $$lex1($$lex, &$$lval) + } + $$n += $$char + if $$n < 0 || $$n >= $$Last { + goto $$default + } + $$n = $$Act[$$n] + if $$Chk[$$n] == $$char { /* valid shift */ + $$char = -1 + $$VAL = $$lval + $$state = $$n + if Errflag > 0 { + Errflag-- + } + goto $$stack + } + +$$default: + /* default state action */ + $$n = $$Def[$$state] + if $$n == -2 { + if $$char < 0 { + $$char = $$lex1($$lex, &$$lval) + } + + /* look through exception table */ + xi := 0 + for { + if $$Exca[xi+0] == -1 && $$Exca[xi+1] == $$state { + break + } + xi += 2 + } + for xi += 2; ; xi += 2 { + $$n = $$Exca[xi+0] + if $$n < 0 || $$n == $$char { + break + } + } + $$n = $$Exca[xi+1] + if $$n < 0 { + goto ret0 + } + } + if $$n == 0 { + /* error ... attempt to resume parsing */ + switch Errflag { + case 0: /* brand new error */ + $$lex.Error("syntax error") + Nerrs++ + if $$Debug >= 1 { + fmt.Printf("%s", $$Statname($$state)) + fmt.Printf("saw %s\n", $$Tokname($$char)) + } + fallthrough + + case 1, 2: /* incompletely recovered error ... try again */ + Errflag = 3 + + /* find a state where "error" is a legal shift action */ + for $$p >= 0 { + $$n = $$Pact[$$S[$$p].yys] + $$ErrCode + if $$n >= 0 && $$n < $$Last { + $$state = $$Act[$$n] /* simulate a shift of "error" */ + if $$Chk[$$state] == $$ErrCode { + goto $$stack + } + } + + /* the current p has no shift on "error", pop stack */ + if $$Debug >= 2 { + fmt.Printf("error recovery pops state %d\n", $$S[$$p].yys) + } + $$p-- + } + /* there is no state on the stack with an error shift ... abort */ + goto ret1 + + case 3: /* no shift yet; clobber input char */ + if $$Debug >= 2 { + fmt.Printf("error recovery discards %s\n", $$Tokname($$char)) + } + if $$char == $$EofCode { + goto ret1 + } + $$char = -1 + goto $$newstate /* try again in the same state */ + } + } + + /* reduction by production $$n */ + if $$Debug >= 2 { + fmt.Printf("reduce %v in:\n\t%v\n", $$n, $$Statname($$state)) + } + + $$nt := $$n + $$pt := $$p + _ = $$pt // guard against "declared and not used" + + $$p -= $$R2[$$n] + $$VAL = $$S[$$p+1] + + /* consult goto table to find next state */ + $$n = $$R1[$$n] + $$g := $$Pgo[$$n] + $$j := $$g + $$S[$$p].yys + 1 + + if $$j >= $$Last { + $$state = $$Act[$$g] + } else { + $$state = $$Act[$$j] + if $$Chk[$$state] != -$$n { + $$state = $$Act[$$g] + } + } + // dummy call; replaced with literal code + $$run() + goto $$stack /* stack new state and value */ +} +` |