Skip to content

Commit b617c9d

Browse files
committed
WIP add ranges to fsyaccast
1 parent 910b234 commit b617c9d

File tree

4 files changed

+52
-45
lines changed

4 files changed

+52
-45
lines changed

src/FsLexYacc.Runtime/Lexing.fs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ type Position =
6363
pos_cnum=0 }
6464

6565
type [<Struct>] Range = { startPos: Position; endPos: Position }
66+
with static member Empty = { startPos = Position.Empty; endPos = Position.Empty }
67+
6668
type LexBufferFiller<'char> =
6769
{ fillSync : (LexBuffer<'char> -> unit) option
6870
fillAsync : (LexBuffer<'char> -> Async<unit>) option }

src/FsYacc.Core/fsyaccast.fs

Lines changed: 48 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ open FSharp.Text.Lexing
1515
let (|KeyValue|) (kvp:KeyValuePair<_,_>) = kvp.Key,kvp.Value
1616

1717

18-
type Identifier = string
19-
type Code = string * Position
18+
type Identifier = string * Range
19+
type Code = string * Range
2020
type Associativity = LeftAssoc | RightAssoc | NonAssoc
2121
type Rule = Rule of Identifier list * Identifier option * Code option
2222

@@ -29,16 +29,16 @@ type ParserSpec=
2929
Rules : (Identifier * Rule list) list }
3030

3131

32-
type Terminal = string
33-
type NonTerminal = string
32+
type Terminal = string * Range
33+
type NonTerminal = string * Range
3434
type Symbol = Terminal of Terminal | NonTerminal of NonTerminal
3535
type Symbols = Symbol list
3636

3737

3838
//---------------------------------------------------------------------
3939
// Output Raw Parser Spec AST
4040

41-
let StringOfSym sym = match sym with Terminal s -> "'" ^ s ^ "'" | NonTerminal s -> s
41+
let StringOfSym sym = match sym with Terminal (name, range) -> "'" ^ name ^ "'" | NonTerminal (name, range) -> name
4242

4343
let OutputSym os sym = fprintf os "%s" (StringOfSym sym)
4444

@@ -77,14 +77,14 @@ let ProcessParserSpecAst (spec: ParserSpec) =
7777
|> List.mapi (fun n precSpecs -> precSpecs |> List.map (fun (precSym, assoc) -> precSym,ExplicitPrec (assoc, 9999 - n)))
7878
|> List.concat
7979

80-
for (key,_) in explicitPrecInfo |> Seq.countBy fst |> Seq.filter (fun (_,n) -> n > 1) do
80+
for ((key,_),_) in explicitPrecInfo |> Seq.countBy fst |> Seq.filter (fun (_,n) -> n > 1) do
8181
failwithf "%s is given two associativities" key
8282

8383
let explicitPrecInfo =
8484
explicitPrecInfo |> Map.ofList
8585

8686
let implicitSymPrecInfo = NoPrecedence
87-
let terminals = List.map fst spec.Tokens @ ["error"]
87+
let terminals = List.map fst spec.Tokens @ [ "error", Range.Empty ]
8888
let terminalSet = Set.ofList terminals
8989
let IsTerminal z = terminalSet.Contains(z)
9090
let prec_of_terminal sym implicitPrecInfo =
@@ -96,7 +96,9 @@ let ProcessParserSpecAst (spec: ParserSpec) =
9696
spec.Rules |> List.mapi (fun i (nonterm,rules) ->
9797
rules |> List.mapi (fun j (Rule(syms,precsym,code)) ->
9898
let precInfo =
99-
let precsym = List.foldBack (fun x acc -> match acc with Some _ -> acc | None -> match x with z when IsTerminal z -> Some z | _ -> acc) syms precsym
99+
let precsym =
100+
(syms, precsym)
101+
||> List.foldBack (fun x acc -> match acc with Some _ -> acc | None -> match x with z when IsTerminal z -> Some z | _ -> acc)
100102
let implicitPrecInfo = NoPrecedence
101103
match precsym with
102104
| None -> implicitPrecInfo
@@ -105,19 +107,19 @@ let ProcessParserSpecAst (spec: ParserSpec) =
105107
|> List.concat
106108
let nonTerminals = List.map fst spec.Rules
107109
let nonTerminalSet = Set.ofList nonTerminals
108-
let checkNonTerminal nt =
109-
if nt <> "error" && not (nonTerminalSet.Contains(nt)) then
110-
failwith (sprintf "NonTerminal '%s' has no productions" nt)
110+
let checkNonTerminal ((name, range) as nt) =
111+
if name <> "error" && not (nonTerminalSet.Contains(nt)) then
112+
failwith (sprintf "NonTerminal '%s'(%d,%d)-(%d,%d) has no productions" name range.startPos.Line range.startPos.Column range.endPos.Line range.endPos.Column)
111113

112114
for (Production(nt,_,syms,_)) in prods do
113115
for sym in syms do
114116
match sym with
115117
| NonTerminal nt ->
116118
checkNonTerminal nt
117-
| Terminal t ->
118-
if not (IsTerminal t) then failwith (sprintf "token %s is not declared" t)
119+
| Terminal (name, range) as t ->
120+
if not (IsTerminal t) then failwith (sprintf "token %s is not declared" name)
119121

120-
if spec.StartSymbols= [] then (failwith "at least one %start declaration is required");
122+
if spec.StartSymbols = [] then (failwith "at least one start declaration is required");
121123

122124
for (nt,_) in spec.Types do
123125
checkNonTerminal nt;
@@ -242,7 +244,7 @@ type NonTerminalTable(nonTerminals:NonTerminal list) =
242244
let nonterminalsWithIdxs = List.mapi (fun (i:NonTerminalIndex) n -> (i,n)) nonTerminals
243245
let nonterminalIdxs = List.map fst nonterminalsWithIdxs
244246
let a = Array.ofList nonTerminals
245-
let b = CreateDictionary [ for i,x in nonterminalsWithIdxs -> x,i ];
247+
let b = CreateDictionary [ for i,(name, range) in nonterminalsWithIdxs -> name,i ];
246248
member table.OfIndex(i) = a.[i]
247249
member table.ToIndex(i) = b.[i]
248250
member table.Indexes = nonterminalIdxs
@@ -253,30 +255,30 @@ type TerminalTable(terminals:(Terminal * PrecedenceInfo) list) =
253255
let terminalIdxs = List.map fst terminalsWithIdxs
254256
let a = Array.ofList (List.map fst terminals)
255257
let b = Array.ofList (List.map snd terminals)
256-
let c = CreateDictionary [ for i,x in terminalsWithIdxs -> x,i ]
258+
let c = CreateDictionary [ for i, (name, range) in terminalsWithIdxs -> name, i ]
257259

258260
member table.OfIndex(i) = a.[i]
259261
member table.PrecInfoOfIndex(i) = b.[i]
260262
member table.ToIndex(i) = c.[i]
261263
member table.Indexes = terminalIdxs
262264

263265
/// Allocate indexes for each production
264-
type ProductionTable(ntTab:NonTerminalTable, termTab:TerminalTable, nonTerminals:string list, prods: Production list) =
266+
type ProductionTable(ntTab: NonTerminalTable, termTab: TerminalTable, nonTerminals: NonTerminal list, prods: Production list) =
265267
let prodsWithIdxs = List.mapi (fun i n -> (i,n)) prods
266268
let a =
267269
prodsWithIdxs
268270
|> List.map(fun (_,Production(_,_,syms,_)) ->
269271
syms
270272
|> Array.ofList
271273
|> Array.map (function
272-
| Terminal t -> PTerminal (termTab.ToIndex t)
273-
| NonTerminal nt -> PNonTerminal (ntTab.ToIndex nt )) )
274+
| Terminal (name, _) -> PTerminal (termTab.ToIndex name)
275+
| NonTerminal (name, _) -> PNonTerminal (ntTab.ToIndex name )) )
274276
|> Array.ofList
275-
let b = Array.ofList (List.map (fun (_,Production(nt,_,_,_)) -> ntTab.ToIndex nt) prodsWithIdxs)
277+
let b = Array.ofList (List.map (fun (_,Production((name, _),_,_,_)) -> ntTab.ToIndex name) prodsWithIdxs)
276278
let c = Array.ofList (List.map (fun (_,Production(_,prec,_,_)) -> prec) prodsWithIdxs)
277279
let productions =
278280
nonTerminals
279-
|> List.map(fun nt -> (ntTab.ToIndex nt, List.choose (fun (i,Production(nt2,prec,syms,_)) -> if nt2=nt then Some i else None) prodsWithIdxs))
281+
|> List.map(fun (name1, range1) -> (ntTab.ToIndex name1, List.choose (fun (i, Production((name2, range2),prec,syms,_)) -> if name2 = name1 then Some i else None) prodsWithIdxs))
280282
|> CreateDictionary
281283

282284
member prodTab.Symbols(i) = a.[i]
@@ -357,7 +359,7 @@ type CompiledSpec =
357359
gotoTable: int option [] []
358360
endOfInputTerminalIdx: int
359361
errorTerminalIdx: int
360-
nonTerminals: string list
362+
nonTerminals: NonTerminal list
361363
}
362364

363365
/// Compile a pre-processed LALR parser spec to tables following the Dragon book algorithm
@@ -367,21 +369,24 @@ let CompilerLalrParserSpec logf (spec : ProcessedParserSpec): CompiledSpec =
367369
stopWatch.Start()
368370

369371
// Augment the grammar
370-
let fakeStartNonTerminals = spec.StartSymbols |> List.map(fun nt -> "_start"^nt)
371-
let nonTerminals = fakeStartNonTerminals@spec.NonTerminals
372-
let endOfInputTerminal = "$$"
373-
let dummyLookahead = "#"
372+
let fakeStartNonTerminals = spec.StartSymbols |> List.map(fun (name, r) -> "_start" ^ name, r)
373+
let nonTerminals = fakeStartNonTerminals @ spec.NonTerminals
374+
let endOfInputTerminal = "$$", Range.Empty
375+
let dummyLookahead = "#", Range.Empty
374376
let dummyPrec = NoPrecedence
375-
let terminals = spec.Terminals @ [(dummyLookahead,dummyPrec); (endOfInputTerminal,dummyPrec)]
376-
let prods = List.map2 (fun a b -> Production(a, dummyPrec,[NonTerminal b],None)) fakeStartNonTerminals spec.StartSymbols @ spec.Productions
377+
let terminals =
378+
spec.Terminals @ [(dummyLookahead, dummyPrec); (endOfInputTerminal, dummyPrec)]
379+
let prods =
380+
List.map2 (fun a b -> Production(a, dummyPrec, [NonTerminal b], None)) fakeStartNonTerminals spec.StartSymbols
381+
@ spec.Productions
377382
let startNonTerminalIdx_to_prodIdx (i:int) = i
378383

379384
// Build indexed tables
380385
let ntTab = NonTerminalTable(nonTerminals)
381386
let termTab = TerminalTable(terminals)
382-
let prodTab = ProductionTable(ntTab,termTab,nonTerminals,prods)
383-
let dummyLookaheadIdx = termTab.ToIndex dummyLookahead
384-
let endOfInputTerminalIdx = termTab.ToIndex endOfInputTerminal
387+
let prodTab = ProductionTable(ntTab, termTab, nonTerminals, prods)
388+
let dummyLookaheadIdx = termTab.ToIndex (fst dummyLookahead)
389+
let endOfInputTerminalIdx = termTab.ToIndex (fst endOfInputTerminal)
385390

386391
let errorTerminalIdx = termTab.ToIndex "error"
387392

@@ -475,12 +480,13 @@ let CompilerLalrParserSpec logf (spec : ProcessedParserSpec): CompiledSpec =
475480
let prodIdx = prodIdx_of_item0 item0
476481
let dotIdx = dotIdx_of_item0 item0
477482
mkItem0(prodIdx,dotIdx+1)
478-
let fakeStartNonTerminalsSet = Set.ofList (fakeStartNonTerminals |> List.map ntTab.ToIndex)
483+
484+
let fakeStartNonTerminalsSet = Set.ofList (fakeStartNonTerminals |> List.map (fst >> ntTab.ToIndex))
479485

480486
let IsStartItem item0 = fakeStartNonTerminalsSet.Contains(ntIdx_of_item0 item0)
481487
let IsKernelItem item0 = (IsStartItem item0 || dotIdx_of_item0 item0 <> 0)
482488

483-
let StringOfSym sym = match sym with PTerminal s -> "'" ^ termTab.OfIndex s ^ "'" | PNonTerminal s -> ntTab.OfIndex s
489+
let StringOfSym sym = match sym with PTerminal s -> "'" ^ fst (termTab.OfIndex s) ^ "'" | PNonTerminal s -> fst (ntTab.OfIndex s)
484490

485491
let OutputSym os sym = fprintf os "%s" (StringOfSym sym)
486492

@@ -489,7 +495,7 @@ let CompilerLalrParserSpec logf (spec : ProcessedParserSpec): CompiledSpec =
489495

490496
// Print items and other stuff
491497
let OutputItem0 os item0 =
492-
fprintf os " %s -> %a . %a" (ntTab.OfIndex (ntIdx_of_item0 item0)) (* outputPrecInfo precInfo *) OutputSyms (lsyms_of_item0 item0) OutputSyms (rsyms_of_item0 item0)
498+
fprintf os " %s -> %a . %a" (fst (ntTab.OfIndex (ntIdx_of_item0 item0))) (* outputPrecInfo precInfo *) OutputSyms (lsyms_of_item0 item0) OutputSyms (rsyms_of_item0 item0)
493499

494500
let OutputItem0Set os s =
495501
Set.iter (fun item -> fprintfn os "%a" OutputItem0 item) s
@@ -503,12 +509,12 @@ let CompilerLalrParserSpec logf (spec : ProcessedParserSpec): CompiledSpec =
503509
let OutputAction os m =
504510
match m with
505511
| Shift n -> fprintf os " shift %d" n
506-
| Reduce prodIdx -> fprintf os " reduce %s --> %a" (ntTab.OfIndex (prodTab.NonTerminal prodIdx)) OutputSyms (prodTab.Symbols prodIdx)
512+
| Reduce prodIdx -> fprintf os " reduce %s --> %a" (fst (ntTab.OfIndex (prodTab.NonTerminal prodIdx))) OutputSyms (prodTab.Symbols prodIdx)
507513
| Error -> fprintf os " error"
508514
| Accept -> fprintf os " accept"
509515

510516
let OutputActions os m =
511-
Array.iteri (fun i (prec,action) -> let term = termTab.OfIndex i in fprintfn os " action '%s' (%a): %a" term outputPrecInfo prec OutputAction action) m
517+
Array.iteri (fun i (prec,action) -> let (name, range) = termTab.OfIndex i in fprintfn os " action '%s' (%a): %a" name outputPrecInfo prec OutputAction action) m
512518

513519
let OutputActionTable os m =
514520
Array.iteri (fun i n -> fprintfn os "state %d:" i; fprintfn os "%a" OutputActions n) m
@@ -519,7 +525,7 @@ let CompilerLalrParserSpec logf (spec : ProcessedParserSpec): CompiledSpec =
519525
| Some a -> OutputAction os a
520526

521527
let OutputGotos os m =
522-
Array.iteri (fun ntIdx s -> let nonterm = ntTab.OfIndex ntIdx in match s with Some st -> fprintfn os " goto %s: %d" nonterm st | None -> ()) m
528+
Array.iteri (fun ntIdx s -> let (name, range) = ntTab.OfIndex ntIdx in match s with Some st -> fprintfn os " goto %s: %d" name st | None -> ()) m
523529

524530
let OutputCombined os m =
525531
Array.iteri (fun i (a,b,c,d) ->
@@ -760,7 +766,7 @@ let CompilerLalrParserSpec logf (spec : ProcessedParserSpec): CompiledSpec =
760766
"reduce", prodTab.Symbols x
761767
|> Array.map StringOfSym
762768
|> String.concat " "
763-
|> sprintf "reduce(%s:%s)" (ntTab.OfIndex nt)
769+
|> sprintf "reduce(%s:%s)" (fst (ntTab.OfIndex nt))
764770
| _ -> "", ""
765771
let pstr =
766772
match p with
@@ -776,7 +782,7 @@ let CompilerLalrParserSpec logf (spec : ProcessedParserSpec): CompiledSpec =
776782
an, "{" + pstr + " " + astr + "}"
777783
let a1n, astr1 = reportAction x1
778784
let a2n, astr2 = reportAction x2
779-
printfn " %s/%s error at state %d on terminal %s between %s and %s - assuming the former because %s" a1n a2n kernelIdx (termTab.OfIndex termIdx) astr1 astr2 reason
785+
printfn " %s/%s error at state %d on terminal %s between %s and %s - assuming the former because %s" a1n a2n kernelIdx (fst (termTab.OfIndex termIdx)) astr1 astr2 reason
780786
match itemSoFar,itemNew with
781787
| (_,Shift s1),(_, Shift s2) ->
782788
if actionSoFar <> actionNew then
@@ -904,11 +910,11 @@ let CompilerLalrParserSpec logf (spec : ProcessedParserSpec): CompiledSpec =
904910

905911
/// The final results
906912
let states = kernels |> Array.ofList
907-
let prods = Array.ofList (List.map (fun (Production(nt,prec,syms,code)) -> (nt, ntTab.ToIndex nt, syms,code)) prods)
913+
let prods = Array.ofList (List.map (fun (Production((name, range) as nt, prec, syms, code)) -> (nt, ntTab.ToIndex name, syms,code)) prods)
908914

909915
logf (fun logStream ->
910916
printfn "writing tables to log"; stdout.Flush();
911-
OutputLalrTables logStream (prods, states, startKernelIdxs, actionTable, immediateActionTable, gotoTable, (termTab.ToIndex endOfInputTerminal), errorTerminalIdx));
917+
OutputLalrTables logStream (prods, states, startKernelIdxs, actionTable, immediateActionTable, gotoTable, (termTab.ToIndex (fst endOfInputTerminal)), errorTerminalIdx));
912918

913919
let states = states |> Array.map (Set.toList >> List.map prodIdx_of_item0)
914920
{ prods = prods
@@ -917,7 +923,7 @@ let CompilerLalrParserSpec logf (spec : ProcessedParserSpec): CompiledSpec =
917923
actionTable = actionTable
918924
immediateActionTable = immediateActionTable
919925
gotoTable = gotoTable
920-
endOfInputTerminalIdx = termTab.ToIndex endOfInputTerminal
926+
endOfInputTerminalIdx = termTab.ToIndex (fst endOfInputTerminal)
921927
errorTerminalIdx = errorTerminalIdx
922928
nonTerminals = nonTerminals }
923929

src/FsYacc.Core/fsyaccpars.fsy

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ spec:
2626

2727
headeropt:
2828
| HEADER
29-
{ $1 }
29+
{ $1, parseState.ResultRange }
3030
|
31-
{ "", (parseState.ResultRange |> fst)}
31+
{ "", parseState.ResultRange }
3232

3333
decls:
3434
{ [] }

tests/FsLex.Core.Tests/FsLex.Core.Tests.fsproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
</PropertyGroup>
88

99
<ItemGroup>
10-
<Compile Include="Sample.fs" />
1110
<Compile Include="Main.fs" />
1211
<Content Include="../../src/FsLex.Core/fsyaccpars.fsy" />
1312
</ItemGroup>

0 commit comments

Comments
 (0)