diff --git a/Tmain/list-fields-with-prefix.d/stdout-expected.txt b/Tmain/list-fields-with-prefix.d/stdout-expected.txt index dcfcdc9ffe..898a490932 100644 --- a/Tmain/list-fields-with-prefix.d/stdout-expected.txt +++ b/Tmain/list-fields-with-prefix.d/stdout-expected.txt @@ -19,6 +19,7 @@ x UCTAGSxpath no NONE s-- no -- xpath for - UCTAGSmacrodef no CPreProcessor s-- no -- macro definition - UCTAGSmacrodef no CUDA s-- no -- macro definition - UCTAGSproperties no CUDA s-- no -- properties (static, inline, mutable,...) +- UCTAGSmoduleName yes Elm s-- no -- actual name of renamed module - UCTAGSannotations yes GDScript s-- no -- annotations on functions and variables - UCTAGShowImported no Go s-- no -- how the package is imported ("inline" for `.' or "init" for `_') - UCTAGSpackage yes Go s-- no -- the real package specified by the package name diff --git a/Tmain/list-fields.d/stdout-expected.txt b/Tmain/list-fields.d/stdout-expected.txt index 7d48f48e8f..d05ad961de 100644 --- a/Tmain/list-fields.d/stdout-expected.txt +++ b/Tmain/list-fields.d/stdout-expected.txt @@ -37,6 +37,7 @@ z kind no NONE s-- no r- [tags output] prepend "kind:" to k/ (or K/) field outpu - macrodef no CPreProcessor s-- no -- macro definition - macrodef no CUDA s-- no -- macro definition - properties no CUDA s-- no -- properties (static, inline, mutable,...) +- moduleName yes Elm s-- no -- actual name of renamed module - annotations yes GDScript s-- no -- annotations on functions and variables - howImported no Go s-- no -- how the package is imported ("inline" for `.' or "init" for `_') - package yes Go s-- no -- the real package specified by the package name diff --git a/Tmain/list-roles.d/stdout-expected.txt b/Tmain/list-roles.d/stdout-expected.txt index 2768d11872..3d29e154b9 100644 --- a/Tmain/list-roles.d/stdout-expected.txt +++ b/Tmain/list-roles.d/stdout-expected.txt @@ -40,7 +40,10 @@ DTD e/element attOwner on attributes owner DTD p/parameterEntity condition on conditions DTD p/parameterEntity elementName on element names DTD p/parameterEntity partOfAttDef on part of attribute definition -Elm m/module imported on imported module +Elm c/constructor imported on item imported +Elm f/function imported on item imported +Elm m/module imported on item imported +Elm t/type imported on item imported Flex I/import import on imports GDScript c/class extended on used as a base class for extending GemSpec g/gem develDep on specifying development dependency @@ -145,7 +148,10 @@ DTD e/element attOwner on attributes owner DTD p/parameterEntity condition on conditions DTD p/parameterEntity elementName on element names DTD p/parameterEntity partOfAttDef on part of attribute definition -Elm m/module imported on imported module +Elm c/constructor imported on item imported +Elm f/function imported on item imported +Elm m/module imported on item imported +Elm t/type imported on item imported Flex I/import import on imports GDScript c/class extended on used as a base class for extending GemSpec g/gem develDep on specifying development dependency diff --git a/Units/parser-elm.r/elm-aliases.d/args.ctags b/Units/parser-elm.r/elm-aliases.d/args.ctags new file mode 100644 index 0000000000..66c8fec799 --- /dev/null +++ b/Units/parser-elm.r/elm-aliases.d/args.ctags @@ -0,0 +1,2 @@ +--sort=no +--fields=+r diff --git a/Units/parser-elm.r/elm-aliases.d/expected.tags b/Units/parser-elm.r/elm-aliases.d/expected.tags new file mode 100644 index 0000000000..b483a30094 --- /dev/null +++ b/Units/parser-elm.r/elm-aliases.d/expected.tags @@ -0,0 +1,3 @@ +MyAlias1 input.elm /^type alias MyAlias1 = String$/;" a roles:def +MyAlias2 input.elm /^type alias MyAlias2 =$/;" a roles:def +MyAlias3 input.elm /^ MyAlias3$/;" a roles:def diff --git a/Units/parser-elm.r/elm-aliases.d/input.elm b/Units/parser-elm.r/elm-aliases.d/input.elm new file mode 100644 index 0000000000..d900981f7d --- /dev/null +++ b/Units/parser-elm.r/elm-aliases.d/input.elm @@ -0,0 +1,11 @@ +type alias MyAlias1 = String + +type alias MyAlias2 = + String + +type + alias + MyAlias3 + = + { x:Float, y:Float } + diff --git a/Units/simple-elm.d/args.ctags b/Units/parser-elm.r/elm-bad-lines.d/args.ctags similarity index 100% rename from Units/simple-elm.d/args.ctags rename to Units/parser-elm.r/elm-bad-lines.d/args.ctags index a4ff198709..41f6dbed62 100644 --- a/Units/simple-elm.d/args.ctags +++ b/Units/parser-elm.r/elm-bad-lines.d/args.ctags @@ -1,3 +1,3 @@ --sort=no ---fields=+r --extras=+r +--fields=+r diff --git a/Units/parser-elm.r/elm-bad-lines.d/expected.tags b/Units/parser-elm.r/elm-bad-lines.d/expected.tags new file mode 100644 index 0000000000..fe99fb26ca --- /dev/null +++ b/Units/parser-elm.r/elm-bad-lines.d/expected.tags @@ -0,0 +1,8 @@ +Input input.elm /^import Input exposing (..)$/;" m roles:imported +input1a input-1.elm /^input1a = 1$/;" f roles:def +input1b input-1.elm /^input1b = 2$/;" f typeref:typename:Int roles:def +input2b input-2.elm /^input2b = 1$/;" f roles:def +input2c input-2.elm /^input2c = 3$/;" f roles:def +module2d input-2.elm /^module2d = 4$/;" f roles:def +input3b input-3.elm /^input3b = 1$/;" f roles:def +input3d input-3.elm /^input3d = 1$/;" f roles:def diff --git a/Units/parser-elm.r/elm-bad-lines.d/input-1.elm b/Units/parser-elm.r/elm-bad-lines.d/input-1.elm new file mode 100644 index 0000000000..2393c4b1ba --- /dev/null +++ b/Units/parser-elm.r/elm-bad-lines.d/input-1.elm @@ -0,0 +1,8 @@ +type alias Input1a *= ConsA + +input1a = 1 + +type alias Input1b *= ConsB + +input1b : Int +input1b = 2 diff --git a/Units/parser-elm.r/elm-bad-lines.d/input-2.elm b/Units/parser-elm.r/elm-bad-lines.d/input-2.elm new file mode 100644 index 0000000000..021035bda4 --- /dev/null +++ b/Units/parser-elm.r/elm-bad-lines.d/input-2.elm @@ -0,0 +1,9 @@ +type Input2a = Cons2A1 | Cons2A2 * + +input2b = 1 + +module = 2 + +input2c = 3 + +module2d = 4 diff --git a/Units/parser-elm.r/elm-bad-lines.d/input-3.elm b/Units/parser-elm.r/elm-bad-lines.d/input-3.elm new file mode 100644 index 0000000000..f0246748b4 --- /dev/null +++ b/Units/parser-elm.r/elm-bad-lines.d/input-3.elm @@ -0,0 +1,13 @@ +-- Should be able to ignore a function where only part of the body +-- parses successfully + +input3a i3_1 i3_2 = + (]? x && + +input3b = 1 + +input3c i3_1 i3_2 = + (]? x && + +input3d = 1 + diff --git a/Units/parser-elm.r/elm-bad-lines.d/input.elm b/Units/parser-elm.r/elm-bad-lines.d/input.elm new file mode 100644 index 0000000000..64f7371845 --- /dev/null +++ b/Units/parser-elm.r/elm-bad-lines.d/input.elm @@ -0,0 +1,3 @@ +import Input exposinx?* + +import Input exposing (..) diff --git a/Units/parser-elm.r/elm-case-statements.d/args.ctags b/Units/parser-elm.r/elm-case-statements.d/args.ctags new file mode 100644 index 0000000000..41f6dbed62 --- /dev/null +++ b/Units/parser-elm.r/elm-case-statements.d/args.ctags @@ -0,0 +1,3 @@ +--sort=no +--extras=+r +--fields=+r diff --git a/Units/parser-elm.r/elm-case-statements.d/expected.tags b/Units/parser-elm.r/elm-case-statements.d/expected.tags new file mode 100644 index 0000000000..898bf660e3 --- /dev/null +++ b/Units/parser-elm.r/elm-case-statements.d/expected.tags @@ -0,0 +1,6 @@ +funcA input.elm /^funcA a1 a2 =$/;" f roles:def +funcB input.elm /^funcB b1 =$/;" f roles:def +funcC input.elm /^funcC c1 c2 =$/;" f roles:def +c2 input.elm /^ c2 = 2$/;" f function:funcC roles:def +c3 input.elm /^ c3 = 3$/;" f function:funcC roles:def +funcD input.elm /^funcD str =$/;" f roles:def diff --git a/Units/parser-elm.r/elm-case-statements.d/input.elm b/Units/parser-elm.r/elm-case-statements.d/input.elm new file mode 100644 index 0000000000..d793911444 --- /dev/null +++ b/Units/parser-elm.r/elm-case-statements.d/input.elm @@ -0,0 +1,43 @@ +-- A function with a case statement + +-- The code below won't compile, but it is +-- syntactically correct + +funcA a1 a2 = + case (complex ex pression + 12) of + "Literal" -> + a + b + 'l' -> + Cons a b + Cons a b -> + a + + b + _ -> + False + +-- Check we can still read a simple function + +funcB b1 = + True + +-- Can we catch functions defined inside case statements? + +funcC c1 c2 = + case (complex ex pression + 12) of + "Literal" -> + let + c2 = 2 + c3 = 3 + in + c2 + c3 + _ -> False + +-- This previously exposed a bug... + +funcD str = + case str of + Just c1 -> + c1 / 255 + _ -> + 0 + diff --git a/Units/parser-elm.r/elm-comments.d/args.ctags b/Units/parser-elm.r/elm-comments.d/args.ctags new file mode 100644 index 0000000000..41f6dbed62 --- /dev/null +++ b/Units/parser-elm.r/elm-comments.d/args.ctags @@ -0,0 +1,3 @@ +--sort=no +--extras=+r +--fields=+r diff --git a/Units/parser-elm.r/elm-comments.d/expected.tags b/Units/parser-elm.r/elm-comments.d/expected.tags new file mode 100644 index 0000000000..ecc5bb5045 --- /dev/null +++ b/Units/parser-elm.r/elm-comments.d/expected.tags @@ -0,0 +1,2 @@ +f input.elm /^f = 6 {- another comment -}$/;" f roles:def +h input.elm /^h = 9$/;" f roles:def diff --git a/Units/parser-elm.r/elm-comments.d/input.elm b/Units/parser-elm.r/elm-comments.d/input.elm new file mode 100644 index 0000000000..7f4aaeed93 --- /dev/null +++ b/Units/parser-elm.r/elm-comments.d/input.elm @@ -0,0 +1,31 @@ +-- a = 1 + +{- b = 2 -} + +{- +c = 3 +-} + +{-* d = 4 -} + +{-* +e = 5 + -} + +{- some comment -} +f = 6 {- another comment -} + + -- This should be ignored + +-- This next line is not top level +{- so should continue the above -} + 1 + +-- A top level statement + +h = 9 + +{- +comment + {- nested comment -} +i = 9 +-} diff --git a/Units/parser-elm.r/elm-complex-types.d/args.ctags b/Units/parser-elm.r/elm-complex-types.d/args.ctags new file mode 100644 index 0000000000..0616e13069 --- /dev/null +++ b/Units/parser-elm.r/elm-complex-types.d/args.ctags @@ -0,0 +1,3 @@ +--sort=no +--extras=+r +--fields=+r-t diff --git a/Units/parser-elm.r/elm-complex-types.d/expected.tags b/Units/parser-elm.r/elm-complex-types.d/expected.tags new file mode 100644 index 0000000000..5480df9e6d --- /dev/null +++ b/Units/parser-elm.r/elm-complex-types.d/expected.tags @@ -0,0 +1,20 @@ +A input.elm /^type A = ACons { x : Float, y : Float }$/;" t roles:def +ACons input.elm /^type A = ACons { x : Float, y : Float }$/;" c type:A roles:def +B input.elm /^type B$/;" t roles:def +B1Cons input.elm /^ = B1Cons$/;" c type:B roles:def +B2Cons input.elm /^ | B2Cons String Integer$/;" c type:B roles:def +B3Cons input.elm /^ | B3Cons$/;" c type:B roles:def +C input.elm /^type C=CCons{x:Float,y:Float}$/;" t roles:def +CCons input.elm /^type C=CCons{x:Float,y:Float}$/;" c type:C roles:def +D input.elm /^type D = DCons (String, Float, {x:String, y:Float})$/;" t roles:def +DCons input.elm /^type D = DCons (String, Float, {x:String, y:Float})$/;" c type:D roles:def +E input.elm /^type E$/;" t roles:def +E1Cons input.elm /^ = E1Cons {}$/;" c type:E roles:def +E2Cons input.elm /^ | E2Cons ()$/;" c type:E roles:def +F input.elm /^type F$/;" t roles:def +F1Cons input.elm /^ = F1Cons (String -> Int)$/;" c type:F roles:def +F2Cons input.elm /^ | F2Cons (Float -> String -> (String -> {x:Float, y:Float}))$/;" c type:F roles:def +F3Cons input.elm /^ | F3Cons$/;" c type:F roles:def +G input.elm /^type G a$/;" t roles:def +G1Cons input.elm /^ = G1Cons a Int$/;" c type:G roles:def +G2Cons input.elm /^ | G2Cons { a | name : String}$/;" c type:G roles:def diff --git a/Units/parser-elm.r/elm-complex-types.d/input.elm b/Units/parser-elm.r/elm-complex-types.d/input.elm new file mode 100644 index 0000000000..6977858454 --- /dev/null +++ b/Units/parser-elm.r/elm-complex-types.d/input.elm @@ -0,0 +1,40 @@ +-- A type with a record spec + +type A = ACons { x : Float, y : Float } + +-- A type with more complex spec + +type B + = B1Cons + { x : Float + , y : Float + } + | B2Cons String Integer + | B3Cons + +-- With a lack of spacing + +type C=CCons{x:Float,y:Float} + +-- Tuples + +type D = DCons (String, Float, {x:String, y:Float}) + +-- Empty records and tuples + +type E + = E1Cons {} + | E2Cons () + +-- With functions as type specifications + +type F + = F1Cons (String -> Int) + | F2Cons (Float -> String -> (String -> {x:Float, y:Float})) + | F3Cons + +-- With vertical bars within the type + +type G a + = G1Cons a Int + | G2Cons { a | name : String} diff --git a/Units/parser-elm.r/elm-constructor-signatures.d/args.ctags b/Units/parser-elm.r/elm-constructor-signatures.d/args.ctags new file mode 100644 index 0000000000..41f6dbed62 --- /dev/null +++ b/Units/parser-elm.r/elm-constructor-signatures.d/args.ctags @@ -0,0 +1,3 @@ +--sort=no +--extras=+r +--fields=+r diff --git a/Units/parser-elm.r/elm-constructor-signatures.d/expected.tags b/Units/parser-elm.r/elm-constructor-signatures.d/expected.tags new file mode 100644 index 0000000000..87a8b08013 --- /dev/null +++ b/Units/parser-elm.r/elm-constructor-signatures.d/expected.tags @@ -0,0 +1,4 @@ +B input.elm /^type B$/;" t roles:def +B1Cons input.elm /^ = B1Cons$/;" c type:B typeref:typename:{ x : Float , y : Float } -> B roles:def +B2Cons input.elm /^ | B2Cons String Integer$/;" c type:B typeref:typename:String -> Integer -> B roles:def +B3Cons input.elm /^ | B3Cons$/;" c type:B typeref:typename:B roles:def diff --git a/Units/parser-elm.r/elm-constructor-signatures.d/input.elm b/Units/parser-elm.r/elm-constructor-signatures.d/input.elm new file mode 100644 index 0000000000..a5be3db21e --- /dev/null +++ b/Units/parser-elm.r/elm-constructor-signatures.d/input.elm @@ -0,0 +1,7 @@ +type B + = B1Cons + { x : Float + , y : Float + } + | B2Cons String Integer + | B3Cons diff --git a/Units/parser-elm.r/elm-expressions.d/args.ctags b/Units/parser-elm.r/elm-expressions.d/args.ctags new file mode 100644 index 0000000000..41f6dbed62 --- /dev/null +++ b/Units/parser-elm.r/elm-expressions.d/args.ctags @@ -0,0 +1,3 @@ +--sort=no +--extras=+r +--fields=+r diff --git a/Units/parser-elm.r/elm-expressions.d/expected.tags b/Units/parser-elm.r/elm-expressions.d/expected.tags new file mode 100644 index 0000000000..ebbfaef48a --- /dev/null +++ b/Units/parser-elm.r/elm-expressions.d/expected.tags @@ -0,0 +1,13 @@ +funcA input.elm /^funcA =$/;" f roles:def +funcB input.elm /^funcB = (\\b1 b2 b3 -> 42)$/;" f roles:def +funcC input.elm /^funcC =$/;" f roles:def +funcD input.elm /^funcD =$/;" f roles:def +funcE input.elm /^funcE e1 e2 =$/;" f roles:def +funcF input.elm /^funcF = []$/;" f roles:def +funcG input.elm /^funcG =$/;" f roles:def +funcH input.elm /^funcH h =$/;" f roles:def +funcI input.elm /^funcI i1 i2 =$/;" f roles:def +funcJ1 input.elm /^funcJ1 =$/;" f roles:def +funcJ2 input.elm /^funcJ2 =$/;" f roles:def +funcJ3 input.elm /^funcJ3 =$/;" f roles:def +funcJ4 input.elm /^funcJ4 =$/;" f roles:def diff --git a/Units/parser-elm.r/elm-expressions.d/input.elm b/Units/parser-elm.r/elm-expressions.d/input.elm new file mode 100644 index 0000000000..f489d1a64c --- /dev/null +++ b/Units/parser-elm.r/elm-expressions.d/input.elm @@ -0,0 +1,60 @@ +-- Expressions with parentheses + +funcA = + (23 + 34) + +-- Anonymous functions + +funcB = (\b1 b2 b3 -> 42) + +-- Allow us to call constructors as functions + +funcC = + Cons a b + +-- Allow us to functions and constructors using their module names + +funcD = + (List.Deep.Cons a b) + (Main.add a b) + +-- Basic lists + +funcE e1 e2 = + e1 :: [e2, e1 + e2] + +-- Empty lists + +funcF = [] + +-- Tuples, including an empty tuple + +funcG = + (12, 0x034, ('a')) ++ () + +-- Record names as function calls + +funcH h = + (3) + (.age h.family.person) + +-- Binary operators as function calls + +funcI i1 i2 = + (++) (i1 // i2) 99 + +-- Records + +funcJ1 = + {x = 2 , y = 3 , z = 4} + +funcJ2 = + { point | x = point.x + 1 , y = Cons a b } + +funcJ3 = + {} + +-- Record with spaces + +funcJ4 = + { model + | width = width + } diff --git a/Units/parser-elm.r/elm-functions.d/args.ctags b/Units/parser-elm.r/elm-functions.d/args.ctags new file mode 100644 index 0000000000..41f6dbed62 --- /dev/null +++ b/Units/parser-elm.r/elm-functions.d/args.ctags @@ -0,0 +1,3 @@ +--sort=no +--extras=+r +--fields=+r diff --git a/Units/parser-elm.r/elm-functions.d/expected.tags b/Units/parser-elm.r/elm-functions.d/expected.tags new file mode 100644 index 0000000000..e34fb407b2 --- /dev/null +++ b/Units/parser-elm.r/elm-functions.d/expected.tags @@ -0,0 +1,5 @@ +funcA input.elm /^funcA a1 a2 = a1 + a2$/;" f roles:def +funcB input.elm /^funcB b =$/;" f roles:def +funcC input.elm /^funcC = 2 + 1$/;" f roles:def +funcD input.elm /^funcD d1$/;" f roles:def +funcE input.elm /^funcE=2$/;" f roles:def diff --git a/Units/parser-elm.r/elm-functions.d/input.elm b/Units/parser-elm.r/elm-functions.d/input.elm new file mode 100644 index 0000000000..e52dfc20d9 --- /dev/null +++ b/Units/parser-elm.r/elm-functions.d/input.elm @@ -0,0 +1,22 @@ +-- Simple function with some parameters. + +funcA a1 a2 = a1 + a2 + +-- Function over multiple lines + +funcB b = + b + 1 + +-- Function with no parameter + +funcC = 2 + 1 + +-- Function with whitespace - ideally this will be stripped in the type description + +funcD d1 + d2 = d1 + d2 + +-- Function with no whitespace after the name + +funcE=2 + diff --git a/Units/parser-elm.r/elm-if-then-else.d/args.ctags b/Units/parser-elm.r/elm-if-then-else.d/args.ctags new file mode 100644 index 0000000000..41f6dbed62 --- /dev/null +++ b/Units/parser-elm.r/elm-if-then-else.d/args.ctags @@ -0,0 +1,3 @@ +--sort=no +--extras=+r +--fields=+r diff --git a/Units/parser-elm.r/elm-if-then-else.d/expected.tags b/Units/parser-elm.r/elm-if-then-else.d/expected.tags new file mode 100644 index 0000000000..a45ae2d0c6 --- /dev/null +++ b/Units/parser-elm.r/elm-if-then-else.d/expected.tags @@ -0,0 +1,5 @@ +funcA input.elm /^funcA a1 a2 =$/;" f roles:def +funcB input.elm /^funcB b1 =$/;" f roles:def +funcC input.elm /^funcC c1 c2 =$/;" f roles:def +c2 input.elm /^ c2 = 2$/;" f function:funcC roles:def +c3 input.elm /^ c3 = 3$/;" f function:funcC roles:def diff --git a/Units/parser-elm.r/elm-if-then-else.d/input.elm b/Units/parser-elm.r/elm-if-then-else.d/input.elm new file mode 100644 index 0000000000..9178a59843 --- /dev/null +++ b/Units/parser-elm.r/elm-if-then-else.d/input.elm @@ -0,0 +1,31 @@ +-- A function with an if/then/else statement + +-- The code below won't compile, but it is +-- syntactically correct + +funcA a1 a2 = + if (complex ex pression + 12) == "Literal" + then + a + b + else + a + + b + +-- Check we can still read a simple function? + +funcB b1 = + True + +-- Can we catch functions defined inside if statements? + +funcC c1 c2 = + if (complex ex pression + 12) == "Literal" + then + let + c2 = 2 + c3 = 3 + in + c2 + c3 + else + a + + b diff --git a/Units/parser-elm.r/elm-imports.d/args.ctags b/Units/parser-elm.r/elm-imports.d/args.ctags new file mode 100644 index 0000000000..0616e13069 --- /dev/null +++ b/Units/parser-elm.r/elm-imports.d/args.ctags @@ -0,0 +1,3 @@ +--sort=no +--extras=+r +--fields=+r-t diff --git a/Units/parser-elm.r/elm-imports.d/expected.tags b/Units/parser-elm.r/elm-imports.d/expected.tags new file mode 100644 index 0000000000..1f2d61ee28 --- /dev/null +++ b/Units/parser-elm.r/elm-imports.d/expected.tags @@ -0,0 +1,23 @@ +SomeMod input.elm /^module SomeMod exposing (..)$/;" m roles:def +PlainImport input.elm /^import PlainImport$/;" m roles:imported +MyMod input.elm /^import MyMod exposing$/;" m roles:imported +map input.elm /^ ( map, foldl$/;" f module:MyMod roles:imported +foldl input.elm /^ ( map, foldl$/;" f module:MyMod roles:imported +Maybe input.elm /^ , Maybe, Possibly$/;" t module:MyMod roles:imported +Possibly input.elm /^ , Maybe, Possibly$/;" t module:MyMod roles:imported +Result input.elm /^ , Result(..)$/;" t module:MyMod roles:imported +MyList input.elm /^ , MyList(Empty), Tree(Node, Value, Special) )$/;" t module:MyMod roles:imported +Empty input.elm /^ , MyList(Empty), Tree(Node, Value, Special) )$/;" c type:MyMod.MyList roles:imported +Tree input.elm /^ , MyList(Empty), Tree(Node, Value, Special) )$/;" t module:MyMod roles:imported +Node input.elm /^ , MyList(Empty), Tree(Node, Value, Special) )$/;" c type:MyMod.Tree roles:imported +Value input.elm /^ , MyList(Empty), Tree(Node, Value, Special) )$/;" c type:MyMod.Tree roles:imported +Special input.elm /^ , MyList(Empty), Tree(Node, Value, Special) )$/;" c type:MyMod.Tree roles:imported +otherMod input.elm /^import otherMod exposing (Coin)$/;" m roles:imported +Coin input.elm /^import otherMod exposing (Coin)$/;" t module:otherMod roles:imported +Dotted.name.Here input.elm /^import Dotted.name.Here exposing (Dot(Cons))$/;" m roles:imported +Dot input.elm /^import Dotted.name.Here exposing (Dot(Cons))$/;" t module:Dotted.name.Here roles:imported +Cons input.elm /^import Dotted.name.Here exposing (Dot(Cons))$/;" c type:Dotted.name.Here.Dot roles:imported +func input.elm /^func x =$/;" f module:SomeMod roles:def +String input-1.elm /^import String$/;" m roles:imported +A input-1.elm /^type A = B$/;" t roles:def +B input-1.elm /^type A = B$/;" c type:A roles:def diff --git a/Units/parser-elm.r/elm-imports.d/input-1.elm b/Units/parser-elm.r/elm-imports.d/input-1.elm new file mode 100644 index 0000000000..27c2d745f5 --- /dev/null +++ b/Units/parser-elm.r/elm-imports.d/input-1.elm @@ -0,0 +1,3 @@ +import String + +type A = B diff --git a/Units/parser-elm.r/elm-imports.d/input.elm b/Units/parser-elm.r/elm-imports.d/input.elm new file mode 100644 index 0000000000..3f04ecf72d --- /dev/null +++ b/Units/parser-elm.r/elm-imports.d/input.elm @@ -0,0 +1,21 @@ +module SomeMod exposing (..) + +import PlainImport + +import MyMod exposing + ( map, foldl + , Maybe, Possibly + , Result(..) + , MyList(Empty), Tree(Node, Value, Special) ) + +-- Allow a bit of looseness in module naming, even though the +-- compiler will reject it + +import otherMod exposing (Coin) + +-- Allow a dotted module name + +import Dotted.name.Here exposing (Dot(Cons)) + +func x = + 42 + 24 diff --git a/Units/parser-elm.r/elm-just-comments.d/expected.tags b/Units/parser-elm.r/elm-just-comments.d/expected.tags new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Units/parser-elm.r/elm-just-comments.d/input.elm b/Units/parser-elm.r/elm-just-comments.d/input.elm new file mode 100644 index 0000000000..472ddac200 --- /dev/null +++ b/Units/parser-elm.r/elm-just-comments.d/input.elm @@ -0,0 +1,21 @@ +-- a = 1 + +{- b = 2 -} + +{- +c = 3 +-} + +{-* d = 4 -} + +{-* +e = 5 + -} + +{- comment -} {- another comment -} + +{- +comment + {- nested comment -} +g = 7 +-} diff --git a/Units/parser-elm.r/elm-let-in.d/args.ctags b/Units/parser-elm.r/elm-let-in.d/args.ctags new file mode 100644 index 0000000000..41f6dbed62 --- /dev/null +++ b/Units/parser-elm.r/elm-let-in.d/args.ctags @@ -0,0 +1,3 @@ +--sort=no +--extras=+r +--fields=+r diff --git a/Units/parser-elm.r/elm-let-in.d/expected.tags b/Units/parser-elm.r/elm-let-in.d/expected.tags new file mode 100644 index 0000000000..5390164f88 --- /dev/null +++ b/Units/parser-elm.r/elm-let-in.d/expected.tags @@ -0,0 +1,12 @@ +funcA input.elm /^funcA =$/;" f roles:def +a1 input.elm /^ a1 = 1$/;" f function:funcA roles:def +a2 input.elm /^ a2 x y = x + y$/;" f function:funcA roles:def +funcB input.elm /^funcB b =$/;" f roles:def +b1 input.elm /^ b1 =$/;" f function:funcB roles:def +b2 input.elm /^ b2 x y =$/;" f function:funcB roles:def +b3 input.elm /^ b3 x y =$/;" f function:funcB roles:def +b4 input.elm /^ b4 v w =$/;" f function:funcB roles:def +b5 input.elm /^ b5 i j = i + j$/;" f function:funcB roles:def +funcC input.elm /^funcC =$/;" f roles:def +c1 input.elm /^ c1 = 1$/;" f function:funcC roles:def +c2 input.elm /^ c2 x y = x + y$/;" f function:funcC roles:def diff --git a/Units/parser-elm.r/elm-let-in.d/input.elm b/Units/parser-elm.r/elm-let-in.d/input.elm new file mode 100644 index 0000000000..054e3cc179 --- /dev/null +++ b/Units/parser-elm.r/elm-let-in.d/input.elm @@ -0,0 +1,40 @@ +-- Basic let/in block + +funcA = + let + a1 = 1 + a2 x y = x + y + in + a1 << a2 + +-- Nested let/in blocks + +funcB b = + let + b1 = + let + b2 x y = + let + b3 x y = + x * y + b4 v w = + v * w + in + b3 b2 99 + in + (flip << b2) 77 + b5 i j = i + j + in + b1 b + +-- Let/in block with type annotation + +funcC = + let + c1 : Int + c1 = 1 + + c2 : Int Float -> Something + c2 x y = x + y + in + c1 << c2 diff --git a/Units/parser-elm.r/elm-modules.d/args.ctags b/Units/parser-elm.r/elm-modules.d/args.ctags new file mode 100644 index 0000000000..41f6dbed62 --- /dev/null +++ b/Units/parser-elm.r/elm-modules.d/args.ctags @@ -0,0 +1,3 @@ +--sort=no +--extras=+r +--fields=+r diff --git a/Units/parser-elm.r/elm-modules.d/expected.tags b/Units/parser-elm.r/elm-modules.d/expected.tags new file mode 100644 index 0000000000..a56b934199 --- /dev/null +++ b/Units/parser-elm.r/elm-modules.d/expected.tags @@ -0,0 +1,11 @@ +Amodule input.elm /^module Amodule exposing (expo)$/;" m roles:def +someFunc input.elm /^someFunc a =$/;" f module:Amodule roles:def +A1module input-1.elm /^module A1module exposing (expo)$/;" m roles:def +someFunc1 input-1.elm /^someFunc1 a =$/;" f module:A1module roles:def +expo input-1.elm /^expo a =$/;" f module:A1module roles:def +A2_1 input-2.elm /^module A2_1 exposing (expo)$/;" m roles:def +func2_1 input-2.elm /^func2_1 a =$/;" f module:A2_1 roles:def +func2_2 input-2.elm /^func2_2 a =$/;" f module:A2_1 roles:def +A3Module input-3.elm /^module A3Module exposing$/;" m roles:def +a4Module input-4.elm /^module a4Module exposing (..)$/;" m roles:def +a5.Module.Deep input-5.elm /^module a5.Module.Deep exposing (..)$/;" m roles:def diff --git a/Units/parser-elm.r/elm-modules.d/input-1.elm b/Units/parser-elm.r/elm-modules.d/input-1.elm new file mode 100644 index 0000000000..edcbac89c6 --- /dev/null +++ b/Units/parser-elm.r/elm-modules.d/input-1.elm @@ -0,0 +1,7 @@ +module A1module exposing (expo) + +someFunc1 a = + 42 + 24 + +expo a = + 42 + 24 diff --git a/Units/parser-elm.r/elm-modules.d/input-2.elm b/Units/parser-elm.r/elm-modules.d/input-2.elm new file mode 100644 index 0000000000..5ebb86e484 --- /dev/null +++ b/Units/parser-elm.r/elm-modules.d/input-2.elm @@ -0,0 +1,14 @@ +{- Only the first module declaration should be recognised -} + +module A2_1 exposing (expo) + +module A2_2 exposing (expo) + +func2_1 a = + 42 + 24 + +module A2_3 exposing (expo) + +func2_2 a = + 42 + 24 + diff --git a/Units/parser-elm.r/elm-modules.d/input-3.elm b/Units/parser-elm.r/elm-modules.d/input-3.elm new file mode 100644 index 0000000000..4c1c210084 --- /dev/null +++ b/Units/parser-elm.r/elm-modules.d/input-3.elm @@ -0,0 +1,8 @@ +-- Should be able to parse multiple exposed items in a module declaration + +module A3Module exposing + ( map, foldl + , Maybe, Possibly + , Result(..) + , MyList(Empty), Tree(Node, Value, Special) ) + diff --git a/Units/parser-elm.r/elm-modules.d/input-4.elm b/Units/parser-elm.r/elm-modules.d/input-4.elm new file mode 100644 index 0000000000..9697253ce8 --- /dev/null +++ b/Units/parser-elm.r/elm-modules.d/input-4.elm @@ -0,0 +1,3 @@ +-- Be relaxed about module name capitalisation + +module a4Module exposing (..) diff --git a/Units/parser-elm.r/elm-modules.d/input-5.elm b/Units/parser-elm.r/elm-modules.d/input-5.elm new file mode 100644 index 0000000000..c169128906 --- /dev/null +++ b/Units/parser-elm.r/elm-modules.d/input-5.elm @@ -0,0 +1,3 @@ +-- Be relaxed about module name capitalisation + +module a5.Module.Deep exposing (..) diff --git a/Units/parser-elm.r/elm-modules.d/input.elm b/Units/parser-elm.r/elm-modules.d/input.elm new file mode 100644 index 0000000000..662a090f14 --- /dev/null +++ b/Units/parser-elm.r/elm-modules.d/input.elm @@ -0,0 +1,4 @@ +module Amodule exposing (expo) + +someFunc a = + 42 + 24 diff --git a/Units/parser-elm.r/elm-multiline-strings.d/args.ctags b/Units/parser-elm.r/elm-multiline-strings.d/args.ctags new file mode 100644 index 0000000000..41f6dbed62 --- /dev/null +++ b/Units/parser-elm.r/elm-multiline-strings.d/args.ctags @@ -0,0 +1,3 @@ +--sort=no +--extras=+r +--fields=+r diff --git a/Units/parser-elm.r/elm-multiline-strings.d/expected.tags b/Units/parser-elm.r/elm-multiline-strings.d/expected.tags new file mode 100644 index 0000000000..08d2dd7691 --- /dev/null +++ b/Units/parser-elm.r/elm-multiline-strings.d/expected.tags @@ -0,0 +1,3 @@ +funcA input.elm /^funcA a1 a2 = a1 + a2$/;" f roles:def +funcB input.elm /^funcB b =$/;" f roles:def +funcD input.elm /^funcD d1 =$/;" f roles:def diff --git a/Units/parser-elm.r/elm-multiline-strings.d/input.elm b/Units/parser-elm.r/elm-multiline-strings.d/input.elm new file mode 100644 index 0000000000..f2513489c3 --- /dev/null +++ b/Units/parser-elm.r/elm-multiline-strings.d/input.elm @@ -0,0 +1,17 @@ +-- We should be able to parse this function + +funcA a1 a2 = a1 + a2 + +-- We should get only funcB, not funcC + +funcB b = + b + """This is a multiline +string, which ends after + +funcC = 3 +""" + +funcD d1 = + (d1 + 34) + +-- The end diff --git a/Units/parser-elm.r/elm-namespaces.d/args.ctags b/Units/parser-elm.r/elm-namespaces.d/args.ctags new file mode 100644 index 0000000000..41f6dbed62 --- /dev/null +++ b/Units/parser-elm.r/elm-namespaces.d/args.ctags @@ -0,0 +1,3 @@ +--sort=no +--extras=+r +--fields=+r diff --git a/Units/parser-elm.r/elm-namespaces.d/expected.tags b/Units/parser-elm.r/elm-namespaces.d/expected.tags new file mode 100644 index 0000000000..88aede10a9 --- /dev/null +++ b/Units/parser-elm.r/elm-namespaces.d/expected.tags @@ -0,0 +1,5 @@ +A1Mod input.elm /^module A1Mod exposing (..)$/;" m roles:def +A1NameSpace input.elm /^import A1Import as A1NameSpace exposing (a1ImpFunc)$/;" n module:A1Mod roles:def moduleName:A1Import +A1Import input.elm /^import A1Import as A1NameSpace exposing (a1ImpFunc)$/;" m roles:imported +a1ImpFunc input.elm /^import A1Import as A1NameSpace exposing (a1ImpFunc)$/;" f module:A1Import roles:imported +func input.elm /^func a1 =$/;" f module:A1Mod roles:def diff --git a/Units/parser-elm.r/elm-namespaces.d/input.elm b/Units/parser-elm.r/elm-namespaces.d/input.elm new file mode 100644 index 0000000000..9c28a9a190 --- /dev/null +++ b/Units/parser-elm.r/elm-namespaces.d/input.elm @@ -0,0 +1,6 @@ +module A1Mod exposing (..) + +import A1Import as A1NameSpace exposing (a1ImpFunc) + +func a1 = + 42 + 24 diff --git a/Units/parser-elm.r/elm-optlist-compatibility.d/README.md b/Units/parser-elm.r/elm-optlist-compatibility.d/README.md new file mode 100644 index 0000000000..36f8878f61 --- /dev/null +++ b/Units/parser-elm.r/elm-optlist-compatibility.d/README.md @@ -0,0 +1,4 @@ +This directory contains the tests from the previous Elm parser, which used +the optlib parser. The tests are included to here to check compatibility. +However, the output from this PEG parser (and hence the contents of +expected.tags) is enhanced. diff --git a/Units/parser-elm.r/elm-optlist-compatibility.d/args.ctags b/Units/parser-elm.r/elm-optlist-compatibility.d/args.ctags new file mode 100644 index 0000000000..c81d3ab95a --- /dev/null +++ b/Units/parser-elm.r/elm-optlist-compatibility.d/args.ctags @@ -0,0 +1,3 @@ +--sort=no +--fields=+r-t +--extras=+r diff --git a/Units/parser-elm.r/elm-optlist-compatibility.d/expected.tags b/Units/parser-elm.r/elm-optlist-compatibility.d/expected.tags new file mode 100644 index 0000000000..c5126b0bee --- /dev/null +++ b/Units/parser-elm.r/elm-optlist-compatibility.d/expected.tags @@ -0,0 +1,18 @@ +Main input.elm /^port module Main exposing (..)$/;" m roles:def +List input.elm /^import List$/;" m roles:imported +Maybe input.elm /^import Maybe exposing (withDefault)$/;" m roles:imported +withDefault input.elm /^import Maybe exposing (withDefault)$/;" f module:Maybe roles:imported +Je input.elm /^import Json.Encode as Je$/;" n module:Main roles:def moduleName:Json.Encode +Json.Encode input.elm /^import Json.Encode as Je$/;" m roles:imported +Thing input.elm /^type Thing$/;" t module:Main roles:def +One input.elm /^ = One$/;" c type:Main.Thing roles:def +Two input.elm /^ | Two Int$/;" c type:Main.Thing roles:def +Param input.elm /^type Param a$/;" t module:Main roles:def +Cons input.elm /^ = Cons a$/;" c type:Main.Param roles:def +Other input.elm /^ | Other a$/;" c type:Main.Param roles:def +Num input.elm /^type alias Num =$/;" a module:Main roles:def +outward input.elm /^port outward : String -> Cmd a$/;" p module:Main roles:def +inward input.elm /^port inward : (b -> a) -> Sub a$/;" p module:Main roles:def +foo input.elm /^foo a =$/;" f module:Main roles:def +bar input.elm /^bar =$/;" f module:Main roles:def +bas input.elm /^ bas =$/;" f function:Main.bar roles:def diff --git a/Units/simple-elm.d/input.elm b/Units/parser-elm.r/elm-optlist-compatibility.d/input.elm similarity index 100% rename from Units/simple-elm.d/input.elm rename to Units/parser-elm.r/elm-optlist-compatibility.d/input.elm diff --git a/Units/parser-elm.r/elm-parameter-capture.d/args.ctags b/Units/parser-elm.r/elm-parameter-capture.d/args.ctags new file mode 100644 index 0000000000..405ad9b9ff --- /dev/null +++ b/Units/parser-elm.r/elm-parameter-capture.d/args.ctags @@ -0,0 +1,3 @@ +--sort=no +--extras=+r +--fields=+rS diff --git a/Units/parser-elm.r/elm-parameter-capture.d/expected.tags b/Units/parser-elm.r/elm-parameter-capture.d/expected.tags new file mode 100644 index 0000000000..6d76c9bc81 --- /dev/null +++ b/Units/parser-elm.r/elm-parameter-capture.d/expected.tags @@ -0,0 +1,7 @@ +funcA input.elm /^funcA a1 a2 =$/;" f signature:a1 a2 roles:def +funcB input.elm /^funcB b1$/;" f typeref:typename:Int -> Int -> Int signature:b1 b2 roles:def +funcC input.elm /^funcC (c1, c2) {c3} (C4Cons c4 c5) =$/;" f signature:(c1, c2) {c3} (C4Cons c4 c5) roles:def +funcD input.elm /^funcD = 4$/;" f signature: roles:def +funcE input.elm /^funcE=5$/;" f signature: roles:def +funcF1 input.elm /^funcF1 =$/;" f signature: roles:def +funcF2 input.elm /^ funcF2 f1 f2 = 6$/;" f function:funcF1 signature:f1 f2 roles:def diff --git a/Units/parser-elm.r/elm-parameter-capture.d/input.elm b/Units/parser-elm.r/elm-parameter-capture.d/input.elm new file mode 100644 index 0000000000..44562f83ed --- /dev/null +++ b/Units/parser-elm.r/elm-parameter-capture.d/input.elm @@ -0,0 +1,33 @@ +-- Simple paramaeters + +funcA a1 a2 = + a1 + a2 + +-- With extra whitespace and type annotation + +funcB : Int -> Int -> Int +funcB b1 + b2 + = + b1 + b2 + +-- Complex parameters + +funcC (c1, c2) {c3} (C4Cons c4 c5) = + c1 + c2 + +-- No parameters + +funcD = 4 + +-- No whitespace + +funcE=5 + +-- Functions inside let/in block + +funcF1 = + let + funcF2 f1 f2 = 6 + in + 6 diff --git a/Units/parser-elm.r/elm-parameter-patterns.d/args.ctags b/Units/parser-elm.r/elm-parameter-patterns.d/args.ctags new file mode 100644 index 0000000000..41f6dbed62 --- /dev/null +++ b/Units/parser-elm.r/elm-parameter-patterns.d/args.ctags @@ -0,0 +1,3 @@ +--sort=no +--extras=+r +--fields=+r diff --git a/Units/parser-elm.r/elm-parameter-patterns.d/expected.tags b/Units/parser-elm.r/elm-parameter-patterns.d/expected.tags new file mode 100644 index 0000000000..54d386eb47 --- /dev/null +++ b/Units/parser-elm.r/elm-parameter-patterns.d/expected.tags @@ -0,0 +1,7 @@ +funcA input.elm /^funcA (a1, _, a2) =$/;" f roles:def +funcB input.elm /^funcB (b1, b2) (b3, b4) =$/;" f roles:def +funcC input.elm /^funcC {c1} {c2, c3} =$/;" f roles:def +funcD input.elm /^funcD (D1Cons a b) (D2Cons a b, D3Cons a b) =$/;" f roles:def +funcE input.elm /^funcE (D1Cons {a, b} c) d =$/;" f roles:def +funcF input.elm /^funcF (D1Cons ({a, b} as ab) c) (d as d2) =$/;" f roles:def +funcG input.elm /^funcG =$/;" f roles:def diff --git a/Units/parser-elm.r/elm-parameter-patterns.d/input.elm b/Units/parser-elm.r/elm-parameter-patterns.d/input.elm new file mode 100644 index 0000000000..bd7c43d339 --- /dev/null +++ b/Units/parser-elm.r/elm-parameter-patterns.d/input.elm @@ -0,0 +1,36 @@ +-- Patterns can be found at https://elmprogramming.com/pattern-matching.html + +-- Simple tuple + +funcA (a1, _, a2) = + a1 + a2 + +-- Multiple tuples + +funcB (b1, b2) (b3, b4) = + b1 + b2 + +-- Records with named fields + +funcC {c1} {c2, c3} = + c1 + c2 + c3 + +-- Constructor patterns + +funcD (D1Cons a b) (D2Cons a b, D3Cons a b) = + b + 1 + +-- Combining the above + +funcE (D1Cons {a, b} c) d = + c + 1 + +-- Using 'as' clauses + +funcF (D1Cons ({a, b} as ab) c) (d as d2) = + c + 1 + +-- Make sure complex parameters can be used in anonymous functions + +funcG = + (\ (D1Cons {a, b} c) d -> a + b + c + d) diff --git a/Units/parser-elm.r/elm-ports.d/args.ctags b/Units/parser-elm.r/elm-ports.d/args.ctags new file mode 100644 index 0000000000..41f6dbed62 --- /dev/null +++ b/Units/parser-elm.r/elm-ports.d/args.ctags @@ -0,0 +1,3 @@ +--sort=no +--extras=+r +--fields=+r diff --git a/Units/parser-elm.r/elm-ports.d/expected.tags b/Units/parser-elm.r/elm-ports.d/expected.tags new file mode 100644 index 0000000000..e101ed4dc0 --- /dev/null +++ b/Units/parser-elm.r/elm-ports.d/expected.tags @@ -0,0 +1,3 @@ +Main input.elm /^port module Main exposing (..)$/;" m roles:def +outgoing input.elm /^port outgoing : Enc.Value -> Cmd msg$/;" p module:Main typeref:typename:Enc.Value -> Cmd msg roles:def +incoming input.elm /^port incoming : (Enc.Value -> msg) -> Sub msg$/;" p module:Main typeref:typename:(Enc.Value -> msg) -> Sub msg roles:def diff --git a/Units/parser-elm.r/elm-ports.d/input.elm b/Units/parser-elm.r/elm-ports.d/input.elm new file mode 100644 index 0000000000..6e6adfa7f9 --- /dev/null +++ b/Units/parser-elm.r/elm-ports.d/input.elm @@ -0,0 +1,6 @@ +port module Main exposing (..) + +port outgoing : Enc.Value -> Cmd msg + +port incoming : (Enc.Value -> msg) -> Sub msg + diff --git a/Units/parser-elm.r/elm-single-expressions.d/args.ctags b/Units/parser-elm.r/elm-single-expressions.d/args.ctags new file mode 100644 index 0000000000..41f6dbed62 --- /dev/null +++ b/Units/parser-elm.r/elm-single-expressions.d/args.ctags @@ -0,0 +1,3 @@ +--sort=no +--extras=+r +--fields=+r diff --git a/Units/parser-elm.r/elm-single-expressions.d/expected.tags b/Units/parser-elm.r/elm-single-expressions.d/expected.tags new file mode 100644 index 0000000000..af9de015d6 --- /dev/null +++ b/Units/parser-elm.r/elm-single-expressions.d/expected.tags @@ -0,0 +1,9 @@ +funcA input.elm /^funcA a1 a2 =$/;" f roles:def +funcB input.elm /^funcB b1 b2 =$/;" f roles:def +funcC input.elm /^funcC c1 =$/;" f roles:def +funcD input.elm /^funcD d1 d2 =$/;" f roles:def +funcE input.elm /^funcE e1 =$/;" f roles:def +funcF input.elm /^funcF f1 =$/;" f roles:def +funcG input.elm /^funcG g1 =$/;" f roles:def +funcH input.elm /^funcH h1 =$/;" f roles:def +funcI input.elm /^funcI i1 =$/;" f roles:def diff --git a/Units/parser-elm.r/elm-single-expressions.d/input.elm b/Units/parser-elm.r/elm-single-expressions.d/input.elm new file mode 100644 index 0000000000..0cf2f2ed12 --- /dev/null +++ b/Units/parser-elm.r/elm-single-expressions.d/input.elm @@ -0,0 +1,50 @@ +-- Binary operators and decimals + +funcA a1 a2 = + a1 + a2 - 3.3 * 3e-3 / .1 + +-- More binary operators + +funcB b1 b2 = + b1 |> b2 >> 3.3 /= 4 + 5 + +-- Hex numbers + +funcC c1 = + 0xfa0 /= 0xFAE0 + +-- Parentheses + +funcD d1 d2 = + (3e4 + (5^7 - 0xe) // .2) + +-- Call prefix functions + +funcE e1 = + e1 + myFunc 3 4 + 5 + +-- Multiline strings + +funcF f1 = + f1 ++ """This is a multiline +string, which ends after +this line +""" + +-- Strings + +funcG g1 = + "Double q\"uote, \n, etc" ++ + "uni\u{04FA2}code" + +-- Characters + +funcH h1 = + 'D' ++ '\"' ++ '\'' ++ '\n' ++ + '\u{04FA2}' + +-- Anonymous functions + +funcI i1 = + (\ x y z -> x+y - z) + diff --git a/Units/parser-elm.r/elm-type-annotations.d/args.ctags b/Units/parser-elm.r/elm-type-annotations.d/args.ctags new file mode 100644 index 0000000000..41f6dbed62 --- /dev/null +++ b/Units/parser-elm.r/elm-type-annotations.d/args.ctags @@ -0,0 +1,3 @@ +--sort=no +--extras=+r +--fields=+r diff --git a/Units/parser-elm.r/elm-type-annotations.d/expected.tags b/Units/parser-elm.r/elm-type-annotations.d/expected.tags new file mode 100644 index 0000000000..eebfafd780 --- /dev/null +++ b/Units/parser-elm.r/elm-type-annotations.d/expected.tags @@ -0,0 +1,9 @@ +funcA input.elm /^funcA a = 1$/;" f typeref:typename:Int -> Float -> Float roles:def +funcB input.elm /^funcB b =$/;" f typeref:typename:String -> Float roles:def +funcC input.elm /^funcC c = 3$/;" f typeref:typename:Int -> {c: String} roles:def +funcD input.elm /^funcD = 4$/;" f typeref:typename:(Int, Int) -> String roles:def +funcE input.elm /^funcE = 5$/;" f typeref:typename:Float -> (Int -> Int -> Int -> String) roles:def +funcF input.elm /^funcF = 6$/;" f typeref:typename:String -> {- Old ->-} Int roles:def +funcG input.elm /^funcG = 7$/;" f typeref:typename:G.Int -> G.Other roles:def +funcH input.elm /^funcH h =$/;" f typeref:typename:Int -> Int roles:def +h2 input.elm /^ h2 = 34$/;" f function:funcH roles:def diff --git a/Units/parser-elm.r/elm-type-annotations.d/input.elm b/Units/parser-elm.r/elm-type-annotations.d/input.elm new file mode 100644 index 0000000000..2b076258fb --- /dev/null +++ b/Units/parser-elm.r/elm-type-annotations.d/input.elm @@ -0,0 +1,49 @@ +-- Simple function with a type annotation + +funcA : Int -> Float -> Float +funcA a = 1 + +-- Type annotation over multiple lines + +funcB : + String -> + Float +funcB b = + b + 1 + +-- Function with a record in the type annotation + +funcC : Int -> {c: String} +funcC c = 3 + +-- Function with a tuple in the type annotation + +funcD : (Int, Int) -> String + +funcD = 4 + +-- Functions in the type annotation + +funcE : Float -> (Int -> Int -> Int -> String) + +funcE = 5 + +-- Comments in the type annotation + +funcF : -- Comment + String -> {- Old ->-} Int {-- End--} +funcF = 6 + +-- Dotted types in the type annotation + +funcG : G.Int -> G.Other +funcG = 7 + +-- Functions with type annotations should provide scope + +funcH : Int -> Int +funcH h = + let + h2 = 34 + in + h + h2 diff --git a/Units/parser-elm.r/elm-types.d/args.ctags b/Units/parser-elm.r/elm-types.d/args.ctags new file mode 100644 index 0000000000..0616e13069 --- /dev/null +++ b/Units/parser-elm.r/elm-types.d/args.ctags @@ -0,0 +1,3 @@ +--sort=no +--extras=+r +--fields=+r-t diff --git a/Units/parser-elm.r/elm-types.d/expected.tags b/Units/parser-elm.r/elm-types.d/expected.tags new file mode 100644 index 0000000000..e6a982920e --- /dev/null +++ b/Units/parser-elm.r/elm-types.d/expected.tags @@ -0,0 +1,20 @@ +Apple input.elm /^type Apple = Cox | Braeburn$/;" t roles:def +Cox input.elm /^type Apple = Cox | Braeburn$/;" c type:Apple roles:def +Braeburn input.elm /^type Apple = Cox | Braeburn$/;" c type:Apple roles:def +Box input.elm /^type Box a = Cardboard a | Wooden$/;" t roles:def +Cardboard input.elm /^type Box a = Cardboard a | Wooden$/;" c type:Box roles:def +Wooden input.elm /^type Box a = Cardboard a | Wooden$/;" c type:Box roles:def +Clog input.elm /^type Clog a b = Dutch | English$/;" t roles:def +Dutch input.elm /^type Clog a b = Dutch | English$/;" c type:Clog roles:def +English input.elm /^type Clog a b = Dutch | English$/;" c type:Clog roles:def +DType input.elm /^type DType$/;" t roles:def +D1Cons input.elm /^ = D1Cons { x : String, y:Maybe Int}$/;" c type:DType roles:def +D2Cons input.elm /^ | D2Cons Float Float Clog$/;" c type:DType roles:def +TypesTwo input-1.elm /^module TypesTwo exposing (..)$/;" m roles:def +T2a input-1.elm /^type T2a = T2a_1 | T2a_2$/;" t module:TypesTwo roles:def +T2a_1 input-1.elm /^type T2a = T2a_1 | T2a_2$/;" c type:TypesTwo.T2a roles:def +T2a_2 input-1.elm /^type T2a = T2a_1 | T2a_2$/;" c type:TypesTwo.T2a roles:def +funcT2 input-1.elm /^funcT2 = "T2"$/;" f module:TypesTwo roles:def +T2b input-1.elm /^type T2b = T2b_1 | T2b_2$/;" t module:TypesTwo roles:def +T2b_1 input-1.elm /^type T2b = T2b_1 | T2b_2$/;" c type:TypesTwo.T2b roles:def +T2b_2 input-1.elm /^type T2b = T2b_1 | T2b_2$/;" c type:TypesTwo.T2b roles:def diff --git a/Units/parser-elm.r/elm-types.d/input-1.elm b/Units/parser-elm.r/elm-types.d/input-1.elm new file mode 100644 index 0000000000..30df61a582 --- /dev/null +++ b/Units/parser-elm.r/elm-types.d/input-1.elm @@ -0,0 +1,9 @@ +-- This is to check scoping still works okay + +module TypesTwo exposing (..) + +type T2a = T2a_1 | T2a_2 + +funcT2 = "T2" + +type T2b = T2b_1 | T2b_2 diff --git a/Units/parser-elm.r/elm-types.d/input.elm b/Units/parser-elm.r/elm-types.d/input.elm new file mode 100644 index 0000000000..d9ce206b1a --- /dev/null +++ b/Units/parser-elm.r/elm-types.d/input.elm @@ -0,0 +1,13 @@ +type Apple = Cox | Braeburn + +-- Parameterised types + +type Box a = Cardboard a | Wooden + +type Clog a b = Dutch | English + +-- Constructors with types + +type DType + = D1Cons { x : String, y:Maybe Int} + | D2Cons Float Float Clog diff --git a/Units/simple-elm.d/expected.tags b/Units/simple-elm.d/expected.tags deleted file mode 100644 index 6ea1f7d79c..0000000000 --- a/Units/simple-elm.d/expected.tags +++ /dev/null @@ -1,16 +0,0 @@ -Main input.elm /^port module Main exposing (..)$/;" m roles:def -List input.elm /^import List$/;" m roles:imported -Maybe input.elm /^import Maybe exposing (withDefault)$/;" m roles:imported -Je input.elm /^import Json.Encode as Je$/;" n roles:def -Thing input.elm /^type Thing$/;" t roles:def -One input.elm /^ = One$/;" c type:Thing roles:def -Two input.elm /^ | Two Int$/;" c type:Thing roles:def -Param input.elm /^type Param a$/;" t roles:def -Cons input.elm /^ = Cons a$/;" c type:Param roles:def -Other input.elm /^ | Other a$/;" c type:Param roles:def -Num input.elm /^type alias Num =$/;" a roles:def -outward input.elm /^port outward : String -> Cmd a$/;" p roles:def -inward input.elm /^port inward : (b -> a) -> Sub a$/;" p roles:def -foo input.elm /^foo a =$/;" f roles:def -bar input.elm /^bar =$/;" f roles:def -bas input.elm /^ bas =$/;" f function:bar roles:def diff --git a/docs/man-pages.rst b/docs/man-pages.rst index cae7fad517..8e8a5c1978 100644 --- a/docs/man-pages.rst +++ b/docs/man-pages.rst @@ -15,6 +15,7 @@ Man pages ctags-incompatibilities(7) ctags-faq(7) + ctags-lang-elm(7) ctags-lang-gdscript(7) ctags-lang-inko(7) ctags-lang-iPythonCell(7) diff --git a/docs/man/ctags-lang-elm.7.rst b/docs/man/ctags-lang-elm.7.rst new file mode 100644 index 0000000000..5a9b5845c9 --- /dev/null +++ b/docs/man/ctags-lang-elm.7.rst @@ -0,0 +1,157 @@ +.. _ctags-lang-elm(7): + +============================================================== +ctags-lang-elm +============================================================== + +Random notes about tagging Elm source code with Universal Ctags + +:Version: 5.9.0 +:Manual group: Universal Ctags +:Manual section: 7 + +SYNOPSIS +-------- +| **ctags** ... --languages=+Elm ... +| **ctags** ... --language-force=Elm ... +| **ctags** ... --map-Elm=+.elm ... + +DESCRIPTION +----------- +The Elm parser is a PEG parser using PackCC, which is part of the +ctags infrastructure. It should correctly process all top level +statements, however there is a limitation with functions embedded +in let/in blocks. They will mostly be fine, but sometimes a +function in a let/in block will be omitted. + +EXAMPLES +-------- + +Imports +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Imported items are tagged, but their role is "imported", not "def". + +Imported items are marked as being in the scope of their own module, +not the module that's doing the importing. + +"input.elm" + +.. code-block:: Elm + + module SomeMod exposing (..) + + import MyMod exposing + ( map + , Maybe + , Result(..) + , MyList(Empty) + ) + +"output.tags" +with "--options=NONE -o - --sort=no --extras=+r --fields=+r input.elm" + +.. code-block:: tags + + SomeMod input.elm /^module SomeMod exposing (..)$/;" m roles:def + MyMod input.elm /^import MyMod exposing$/;" m roles:imported + map input.elm /^ ( map$/;" f module:MyMod roles:imported + Maybe input.elm /^ , Maybe$/;" t module:MyMod roles:imported + Result input.elm /^ , Result(..)$/;" t module:MyMod roles:imported + MyList input.elm /^ , MyList(Empty)$/;" t module:MyMod roles:imported + Empty input.elm /^ , MyList(Empty)$/;" c type:MyMod.MyList roles:imported + +Namespaces +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Namespaces are tagged and their role is "def". + +"input.elm" + +.. code-block:: Elm + + module AMod exposing (..) + + import MyImport as NSpace exposing (impFunc) + +"output.tags" +with "--options=NONE -o - --sort=no --extras=+r --fields=+r input.elm" + +.. code-block:: tags + + AMod input.elm /^module AMod exposing (..)$/;" m roles:def + NSpace input.elm /^import MyImport as NSpace exposing (impFunc)$/;" n module:AMod roles:def moduleName:MyImport + MyImport input.elm /^import MyImport as NSpace exposing (impFunc)$/;" m roles:imported + impFunc input.elm /^import MyImport as NSpace exposing (impFunc)$/;" f module:MyImport roles:imported + +Type names +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Constructors top level functions will have type names. + +"input.elm" + +.. code-block:: Elm + + funcA : Int -> Int + funcA a = a + 1 + + type B + = B1Cons + { x : Float + , y : Float + } + | B2Cons String Integer + | B3Cons + +"output.tags" +with "--options=NONE -o - --sort=no --extras=+r --fields=+r input.elm" + +.. code-block:: tags + + funcA input.elm /^funcA a = a + 1$/;" f typeref:typename:Int -> Int roles:def + B input.elm /^type B$/;" t roles:def + B1Cons input.elm /^ = B1Cons$/;" c type:B typeref:typename:{ x : Float , y : Float } -> B roles:def + B2Cons input.elm /^ | B2Cons String Integer$/;" c type:B typeref:typename:String -> Integer -> B roles:def + B3Cons input.elm /^ | B3Cons$/;" c type:B typeref:typename:B roles:def + +Function parameter lists +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Function parameter lists can be extracted into the tags file +signature field. They are not really function signatures, but +it's the closest concept available in ctags. +Use "--fields=+S". + +.. code-block:: Elm + + funcA a1 a2 = + a1 + a2 + +"output.tags" +with "--sort=no --extras=+r --fields=+rS" + +.. code-block:: tags + + funcA input.elm /^funcA a1 a2 =$/;" f signature:a1 a2 roles:def + +KNOWN LIMITATIONS +----------------- +The ctags signature field is used for function parameter lists, even +though it's not an idea field. See above. + +Elm requires all statements at the same logical level to have the +same indentation. If there is additional indentation that line is part +of the previous one. Therefore without over-complicating the +PEG parser we have the following limitations... + +Sometimes functions in let/in blocks will be omitted. + +Functions in let/in blocks will be marked as being in the scope of their +outer function, regardless of how deeply nested the let/in block is. + +Functions in let/in blocks won't have type names. + +SEE ALSO +-------- +:ref:`ctags(1) `, :ref:`ctags-client-tools(7) ` diff --git a/docs/news.rst b/docs/news.rst index 3703ebaeec..a4936efb75 100644 --- a/docs/news.rst +++ b/docs/news.rst @@ -410,7 +410,7 @@ The following parsers have been added: * DTD * DTS * Elixir *optlib* -* Elm *optlib* +* Elm *peg/packcc* * Falcon * FunctionParameters *perl based subparser* * Gdbinit script *optlib* diff --git a/main/parsers_p.h b/main/parsers_p.h index e6f25891a1..424c47c73b 100644 --- a/main/parsers_p.h +++ b/main/parsers_p.h @@ -39,7 +39,8 @@ #define PEG_PARSER_LIST \ VarlinkParser, \ KotlinParser, \ - ThriftParser + ThriftParser, \ + ElmParser #else #define PEG_PARSER_LIST #endif @@ -79,7 +80,6 @@ DosBatchParser, \ EiffelParser, \ ElixirParser, \ - ElmParser, \ EmacsLispParser, \ ErlangParser, \ FalconParser, \ diff --git a/man/Makefile.am b/man/Makefile.am index 7746be1765..52bbe0dbce 100644 --- a/man/Makefile.am +++ b/man/Makefile.am @@ -37,6 +37,7 @@ GEN_IN_MAN_FILES = \ ctags-lang-r.7 \ ctags-lang-rmarkdown.7 \ ctags-lang-sql.7 \ + ctags-lang-elm.7 \ \ readtags.1 \ tags.5 \ diff --git a/man/ctags-lang-elm.7.rst.in b/man/ctags-lang-elm.7.rst.in new file mode 100644 index 0000000000..d9fbfa786d --- /dev/null +++ b/man/ctags-lang-elm.7.rst.in @@ -0,0 +1,157 @@ +.. _ctags-lang-elm(7): + +============================================================== +ctags-lang-elm +============================================================== +------------------------------------------------------------------- +Random notes about tagging Elm source code with Universal Ctags +------------------------------------------------------------------- +:Version: @VERSION@ +:Manual group: Universal Ctags +:Manual section: 7 + +SYNOPSIS +-------- +| **@CTAGS_NAME_EXECUTABLE@** ... --languages=+Elm ... +| **@CTAGS_NAME_EXECUTABLE@** ... --language-force=Elm ... +| **@CTAGS_NAME_EXECUTABLE@** ... --map-Elm=+.elm ... + +DESCRIPTION +----------- +The Elm parser is a PEG parser using PackCC, which is part of the +ctags infrastructure. It should correctly process all top level +statements, however there is a limitation with functions embedded +in let/in blocks. They will mostly be fine, but sometimes a +function in a let/in block will be omitted. + +EXAMPLES +-------- + +Imports +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Imported items are tagged, but their role is "imported", not "def". + +Imported items are marked as being in the scope of their own module, +not the module that's doing the importing. + +"input.elm" + +.. code-block:: Elm + + module SomeMod exposing (..) + + import MyMod exposing + ( map + , Maybe + , Result(..) + , MyList(Empty) + ) + +"output.tags" +with "--options=NONE -o - --sort=no --extras=+r --fields=+r input.elm" + +.. code-block:: tags + + SomeMod input.elm /^module SomeMod exposing (..)$/;" m roles:def + MyMod input.elm /^import MyMod exposing$/;" m roles:imported + map input.elm /^ ( map$/;" f module:MyMod roles:imported + Maybe input.elm /^ , Maybe$/;" t module:MyMod roles:imported + Result input.elm /^ , Result(..)$/;" t module:MyMod roles:imported + MyList input.elm /^ , MyList(Empty)$/;" t module:MyMod roles:imported + Empty input.elm /^ , MyList(Empty)$/;" c type:MyMod.MyList roles:imported + +Namespaces +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Namespaces are tagged and their role is "def". + +"input.elm" + +.. code-block:: Elm + + module AMod exposing (..) + + import MyImport as NSpace exposing (impFunc) + +"output.tags" +with "--options=NONE -o - --sort=no --extras=+r --fields=+r input.elm" + +.. code-block:: tags + + AMod input.elm /^module AMod exposing (..)$/;" m roles:def + NSpace input.elm /^import MyImport as NSpace exposing (impFunc)$/;" n module:AMod roles:def moduleName:MyImport + MyImport input.elm /^import MyImport as NSpace exposing (impFunc)$/;" m roles:imported + impFunc input.elm /^import MyImport as NSpace exposing (impFunc)$/;" f module:MyImport roles:imported + +Type names +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Constructors top level functions will have type names. + +"input.elm" + +.. code-block:: Elm + + funcA : Int -> Int + funcA a = a + 1 + + type B + = B1Cons + { x : Float + , y : Float + } + | B2Cons String Integer + | B3Cons + +"output.tags" +with "--options=NONE -o - --sort=no --extras=+r --fields=+r input.elm" + +.. code-block:: tags + + funcA input.elm /^funcA a = a + 1$/;" f typeref:typename:Int -> Int roles:def + B input.elm /^type B$/;" t roles:def + B1Cons input.elm /^ = B1Cons$/;" c type:B typeref:typename:{ x : Float , y : Float } -> B roles:def + B2Cons input.elm /^ | B2Cons String Integer$/;" c type:B typeref:typename:String -> Integer -> B roles:def + B3Cons input.elm /^ | B3Cons$/;" c type:B typeref:typename:B roles:def + +Function parameter lists +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Function parameter lists can be extracted into the tags file +signature field. They are not really function signatures, but +it's the closest concept available in ctags. +Use "--fields=+S". + +.. code-block:: Elm + + funcA a1 a2 = + a1 + a2 + +"output.tags" +with "--sort=no --extras=+r --fields=+rS" + +.. code-block:: tags + + funcA input.elm /^funcA a1 a2 =$/;" f signature:a1 a2 roles:def + +KNOWN LIMITATIONS +----------------- +The ctags signature field is used for function parameter lists, even +though it's not an idea field. See above. + +Elm requires all statements at the same logical level to have the +same indentation. If there is additional indentation that line is part +of the previous one. Therefore without over-complicating the +PEG parser we have the following limitations... + +Sometimes functions in let/in blocks will be omitted. + +Functions in let/in blocks will be marked as being in the scope of their +outer function, regardless of how deeply nested the let/in block is. + +Functions in let/in blocks won't have type names. + +SEE ALSO +-------- +ctags(1), ctags-client-tools(7) diff --git a/mk_mvc.mak b/mk_mvc.mak index c706e01f56..dec16b7e8e 100644 --- a/mk_mvc.mak +++ b/mk_mvc.mak @@ -101,6 +101,7 @@ main\repoinfo.obj: main\repoinfo.c main\repoinfo.h peg\varlink.c peg\varlink.h: peg\varlink.peg $(PACKCC) peg\kotlin.c peg\kotlin.h: peg\kotlin.peg $(PACKCC) peg\thrift.c peg\thrift.h: peg\thrift.peg $(PACKCC) +peg\elm.c peg\elm.h: peg\elm.peg $(PACKCC) $(RES_OBJ): win32/ctags.rc win32/ctags.exe.manifest win32/resource.h $(RC) /nologo /l 0x409 /Fo$@ $*.rc diff --git a/optlib/elm.c b/optlib/elm.c deleted file mode 100644 index 4fe7ecaca6..0000000000 --- a/optlib/elm.c +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Generated by ./misc/optlib2c from optlib/elm.ctags, Don't edit this manually. - */ -#include "general.h" -#include "parse.h" -#include "routines.h" -#include "field.h" -#include "xtag.h" - - -static void initializeElmParser (const langType language CTAGS_ATTR_UNUSED) -{ -} - -extern parserDefinition* ElmParser (void) -{ - static const char *const extensions [] = { - "elm", - NULL - }; - - static const char *const aliases [] = { - NULL - }; - - static const char *const patterns [] = { - NULL - }; - - static roleDefinition ElmModuleRoleTable [] = { - { true, "imported", "imported module" }, - }; - static kindDefinition ElmKindTable [] = { - { - true, 'm', "module", "Module", - ATTACH_ROLES(ElmModuleRoleTable), - }, - { - true, 'n', "namespace", "Renamed Imported Module", - }, - { - true, 'p', "port", "Port", - }, - { - true, 't', "type", "Type Definition", - }, - { - true, 'c', "constructor", "Type Constructor", - }, - { - true, 'a', "alias", "Type Alias", - }, - { - true, 'f', "function", "Functions", - }, - }; - static tagRegexTable ElmTagRegexTable [] = { - {"^(port[[:blank:]]+)?module[[:blank:]]+([[:upper:]][[:alnum:]_.]*)", "\\2", - "m", "{scope=push}{exclusive}", NULL, false}, - {"^import[[:blank:]]+[[:alnum:]_.]+[[:blank:]]+as[[:blank:]]+([[:alnum:]]+)", "\\1", - "n", "{scope=clear}{exclusive}", NULL, false}, - {"^import[[:blank:]]+([[:alnum:]_.]+)[[:blank:]]exposing", "\\1", - "m", "{scope=clear}{exclusive}{_role=imported}", NULL, false}, - {"^import[[:blank:]]+([[:alnum:]_.]+)", "\\1", - "m", "{scope=clear}{exclusive}{_role=imported}", NULL, false}, - {"^port[[:blank:]]+([[:lower:]][[:alnum:]_]*).*", "\\1", - "p", "{scope=clear}{exclusive}", NULL, false}, - {"^type +([[:upper:]][[:alnum:]_]*).*", "\\1", - "t", "{scope=set}{exclusive}", NULL, false}, - {"^[[:blank:]]+[|=][[:blank:]]+([[:upper:]][[:alnum:]_]*).*$", "\\1", - "c", "{scope=ref}{exclusive}", NULL, false}, - {"^type[[:blank:]]+alias[[:blank:]]+([[:upper:]][[:alnum:]_]*[[:blank:][:alnum:]_]*)", "\\1", - "a", "{scope=set}{exclusive}", NULL, false}, - {"^([[:lower:]_][[:alnum:]_]*)[^=]*=$", "\\1", - "f", "{scope=set}", NULL, false}, - {"^[[:blank:]]+([[:lower:]_][[:alnum:]_]*)[^=]*=$", "\\1", - "f", "{scope=ref}", NULL, false}, - }; - - - parserDefinition* const def = parserNew ("Elm"); - - def->enabled = true; - def->extensions = extensions; - def->patterns = patterns; - def->aliases = aliases; - def->method = METHOD_NOT_CRAFTED|METHOD_REGEX; - def->useCork = CORK_QUEUE; - def->kindTable = ElmKindTable; - def->kindCount = ARRAY_SIZE(ElmKindTable); - def->tagRegexTable = ElmTagRegexTable; - def->tagRegexCount = ARRAY_SIZE(ElmTagRegexTable); - def->initialize = initializeElmParser; - - return def; -} diff --git a/optlib/elm.ctags b/optlib/elm.ctags deleted file mode 100644 index 77290da8d1..0000000000 --- a/optlib/elm.ctags +++ /dev/null @@ -1,45 +0,0 @@ -# -# Copyright (c) 2016, Mark Skipper -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, -# USA. -# -# ---langdef=Elm ---map-Elm=+.elm - ---kinddef-Elm=m,module,Module ---kinddef-Elm=n,namespace,Renamed Imported Module ---kinddef-Elm=p,port,Port ---kinddef-Elm=t,type,Type Definition ---kinddef-Elm=c,constructor,Type Constructor ---kinddef-Elm=a,alias,Type Alias ---kinddef-Elm=f,function,Functions ---_roledef-Elm.{module}=imported,imported module - ---regex-Elm=/^(port[[:blank:]]+)?module[[:blank:]]+([[:upper:]][[:alnum:]_.]*)/\2/m/{scope=push}{exclusive} ---regex-Elm=/^import[[:blank:]]+[[:alnum:]_.]+[[:blank:]]+as[[:blank:]]+([[:alnum:]]+)/\1/n/{scope=clear}{exclusive} -# -# Following two patterns are just for clearing scope stack. -# X in "import X" should be captured as a reference tag in the future. -# ---regex-Elm=/^import[[:blank:]]+([[:alnum:]_.]+)[[:blank:]]exposing/\1/m/{scope=clear}{exclusive}{_role=imported} ---regex-Elm=/^import[[:blank:]]+([[:alnum:]_.]+)/\1/m/{scope=clear}{exclusive}{_role=imported} ---regex-Elm=/^port[[:blank:]]+([[:lower:]][[:alnum:]_]*).*/\1/p/{scope=clear}{exclusive} ---regex-Elm=/^type +([[:upper:]][[:alnum:]_]*).*/\1/t/{scope=set}{exclusive} ---regex-Elm=/^[[:blank:]]+[|=][[:blank:]]+([[:upper:]][[:alnum:]_]*).*$/\1/c/{scope=ref}{exclusive} ---regex-Elm=/^type[[:blank:]]+alias[[:blank:]]+([[:upper:]][[:alnum:]_]*[[:blank:][:alnum:]_]*)/\1/a/{scope=set}{exclusive} ---regex-Elm=/^([[:lower:]_][[:alnum:]_]*)[^=]*=$/\1/f/{scope=set} ---regex-Elm=/^[[:blank:]]+([[:lower:]_][[:alnum:]_]*)[^=]*=$/\1/f/{scope=ref} diff --git a/peg/.gitignore b/peg/.gitignore index 74efb2126f..8d4a72d782 100644 --- a/peg/.gitignore +++ b/peg/.gitignore @@ -1,3 +1,4 @@ varlink.[ch] kotlin.[ch] thrift.[ch] +elm.[ch] diff --git a/peg/elm.peg b/peg/elm.peg new file mode 100644 index 0000000000..bff2a618a9 --- /dev/null +++ b/peg/elm.peg @@ -0,0 +1,671 @@ +# Copyright (c) 2022 Nik Silver +# +# This source code is released for free distribution under the terms of the +# GNU General Public License version 2 or later. +# +# Thanks to: +# - Mark Skipper, for the original Elm optlib parser, which inspired this; +# - Samuel Stauffer, for the Thrift PEG parser, which showed me how to +# write a PEG parser; +# - Jan DolinĂ¡r, for the Kotlin PEG parser, which also provided insight; +# - Masatake YAMATO, for patience and guidance in code reviews. +# +# This parser generates tags for Elm. See https://elm-lang.org/docs/syntax +# for language reference. +# +# The parser will tag items reliably at the top level. Functions +# defined in let/in blocks are also tagged, but with limitations. See below. +# +# Kinds +# - m module +# - n namespace (ie a module that's renamed) +# - t type +# - c constructor (within a type) +# - a alias +# - p port +# - f function +# +# Key/value pairs +# - roles:def This is defined here. +# - roles:imported This is imported here. +# - type: This constructor is in the scope of type , which +# may be dotted. Eg Main.myType. +# - function: This function is in the scope of function , which +# may be dotted. Eg Main.myFunc. +# - module: This is in the scope of module . +# - typeref:description: This function, constructor or port +# has type . +# - moduleName: This namespace has original module name . +# +# Functions defined in let/in blocks may be tagged, with these limitations: +# - the LHS (up to and including the '=') need to be on a single line; +# - the LHS can only have simple parameters; +# - their scope is only marked as being in the top-most function; +# - any type annotation is ignored. +# This should be good for 90% of inner functions. To make it totally robust +# is much more complicated due to (a) Elm's clever indentation-sensitivity +# and (b) limitations of the PEG parser used here. +# +# To do: +# Maybe do: +# - let/in blocks +# - Allow tuples on the LHS. Eg '(val1, val2) = valFunc'. +# - Inner functions' type annotations are used in the function's +# type description. +# - Inner functions can have more complex parameters. +# - Functions +# - Allow non-Latin upper and lower case. Use +# https://util.unicode.org/UnicodeJsps/properties.html +# combined with \p{Lu}, \p{Ll} and \p{L}. +# +# Won't do: +# - Handle Elm's indentation properly. + + +%prefix "pelm" + +%auxil "struct parserCtx *" + +%earlysource { + #include "general.h" +} + +%header { + struct parserCtx; +} + +%source { +#include "elm_pre.h" +#include "routines.h" + +/* + * Include these lines to debug the parsing. + * From https://github.com/arithy/packcc#macros + * This will output parsing info to STDERR.tmp in the vent of a failed test. + */ + +/* +static const char *dbg_str[] = { "Evaluating rule", "Matched rule", "Abandoning rule" }; + +#define PCC_DEBUG(auxil, event, rule, level, pos, buffer, length) \ + fprintf(stderr, "%*s%s %s @%zu [%.*s]\n", \ + (int)((level) * 2), "", dbg_str[event], rule, pos, (int)(length), buffer) + */ +} + +# Top level elements ----------------------------------------------------- + +# We separate the file into the module section and the main section +# so that we only consider and tag one module declaration + +file <- + { + ELM_INIT_MODULE_SCOPE; + } + TLSS? + moduleDeclaration? + TLSS? + mainTopLevelStatements? + TLSS? + EOF + +mainTopLevelStatements <- + topLevelStatement (TLSS topLevelStatement)* + +topLevelStatement <- + importStatement + / typeAlias + / customType + / portDeclaration + / functionWithTypeAnnotation + / functionDefinition + / ignoreRestOfStatement + +# Main Elm grammar ------------------------------------------------------- + +# Module declaration +# +# We can be a bit relaxed about distinguishing functions, types and +# constructors listed in a module declaration, because we're not going +# to tag them. + +moduleDeclaration <- + ('port' _1_)? 'module' _1_ _1_ 'exposing' _0_ '(' exposedList ')' EOS { + elm_module_scope_index = makeElmTagSettingScope(auxil, $1, $1s, K_MODULE, ROLE_DEFINITION_INDEX); + } + +exposedList <- _0_ exposedItem _0_ (',' _0_ exposedList )* + +exposedItem <- + exposedFieldOrType + / exposedFunction + / exposedItemIgnored + +exposedFieldOrType <- + (_0_ '(' _0_ exposedTypeConstructorList _0_ ')')? + +exposedFunction <- + lowerStartIdentifier + +exposedItemIgnored <- '.'+ + +exposedTypeConstructorList <- + (upperStartIdentifier / exposedItemIgnored) _0_ (',' _0_ exposedTypeConstructorList)* + +# Type alias +# +# We don't care what the actual alias is + +typeAlias <- + 'type' _1_ 'alias' _1_ _0_ '=' _0_ ignoreRestOfStatement { + makeElmTag(auxil, $1, $1s, K_ALIAS, ROLE_DEFINITION_INDEX); + } + +# Custom type +# +# Includes type parameters, such as 'x' in 'type MyType x = Wrap x'. +# +# In a definition such as 'type MyType = Cons1 String Int' we +# capture 'MyType', and then for each type in each constructor +# subtype (here, 'String' and 'Int') we append a '->' and finally +# concatentate them all to get the constructor's type description, +# such as 'String -> Int -> MyType' + +customType <- + 'type' _1_ (_0_ typeParameterList)? _0_ '=' _0_ { + initElmConstructorFields(auxil, $1); + makeElmTagSettingScope(auxil, $1, $1s, K_TYPE, ROLE_DEFINITION_INDEX); + } constructorList EOS { + POP_SCOPE(auxil); + tidyElmConstructorFields(auxil); + } + +typeParameterList <- lowerStartIdentifier (_1_ lowerStartIdentifier)* + +# A type could be defined as a constructor list: +# type A = Cons1 String | Cons2 Float Float | ... +# The 'String' and the 'Float Float' etc are the constructor subtypes. +# Each 'String', 'Float', etc is a single type spec. +# But a single type spec could also be a record, a tuple or a function spec. +# +# Subtypes in constructors need to be parsed differently from types in +# type annotations and record fields. Consider these: +# type A1Type a b = A1Cons a b -- Line 1 +# type A2Type a b = A2Cons String a b -- Line 2 +# type BType a b = BCons { x : A2Type a b} -- Line 3 +# cFunc : A1Type String Int -> String -- Line 4 +# In line 1, 'a b' must be parsed as two individual types (parameterised). +# In line 2, 'String a b' must be parsed as three individual types. +# In line 3, 'A2Type a b' must be parsed as one type, even though it's +# lexically equivalent to 'String a b' on line 2. +# In line 4, 'A1Type String Int' must also be parsed one type. +# This means we have to have slightly different rules for parsing a +# constructor's subtypes as from other cases. The first case is handled +# by constructorSubtypeList and singleConstructorSubtypeSpec. The second +# case is handled by singleTypeSpec. + +constructorList <- { + initElmConstructorSubtypeFields(auxil); + } _0_ ? { + int r = makeElmTag(auxil, $1, $1s, K_CONSTRUCTOR, ROLE_DEFINITION_INDEX); + addElmConstructorTypeRef(auxil, r); + } _0_ ('|' _0_ constructorList)? + +constructorSubtypeList <- singleConstructorSubtypeSpec (_0_ singleConstructorSubtypeSpec)* + +singleConstructorSubtypeSpec <- + < recordTypeSpec + / tupleTypeSpec + / functionTypeSpec + / dottedIdentifier + > + { + addElmConstructorSubtype(auxil, $1); + } + +singleTypeSpec <- + recordTypeSpec + / tupleTypeSpec + / functionTypeSpec + / parameterisedTypeSpec + +recordTypeSpec <- + '{' (_0_ recordRestrictionPrefix)? _0_ fieldSpec (_0_ ',' _0_ fieldSpec)* _0_ '}' + / '{' (_0_ recordRestrictionPrefix)? _0_ '}' + +recordRestrictionPrefix <- + lowerStartIdentifier _0_ '|' + +fieldSpec <- + lowerStartIdentifier _0_ ':' _0_ singleTypeSpec + +tupleTypeSpec <- + '(' _0_ singleTypeSpec (_0_ ',' _0_ singleTypeSpec)* _0_ ')' + / '(' _0_ ')' + +parameterisedTypeSpec <- + dottedIdentifier (_1_ (singleTypeSpec / lowerStartIdentifier))* + +functionTypeSpec <- + singleTypeSpec (_0_ '->' _0_ singleTypeSpec)+ + +# Port declaration + +portDeclaration <- + 'port' _1_ _0_ ':' _0_ EOS { + int r = makeElmTag(auxil, $1, $1s, K_PORT, ROLE_DEFINITION_INDEX); + addElmTypeRef(r, $2); + } + +# Import statement +# +# For the import statement we don't want the imported items to appear in the +# scope of the current module (ie this file), otherwise they'll be named +# wrongly. So we # want to save the module scope, make the imported tags, +# then restore the module scope. We do this in two separate C code blocks, +# because the module scope needs to be saved before any of the imported tags +# are made. +# +# Also, if we create a namespace then that *does* live in the scope of the +# current module, so we'll make that tag (if needed) before saving the +# module scope. + +importStatement <- + 'import' _1_ (_1_ 'as' _1_ )? { + // Make the namespace tag first, as it's in the file module's scope + if ($2s > 0) { + int r = makeElmTag(auxil, $2, $2s, K_NAMESPACE, ROLE_DEFINITION_INDEX); + attachParserFieldToCorkEntry (r, ElmFields[F_MODULENAME].ftype, $1); + } + + // Now make the tag for the imported module, as it lives outside + // the scope of the file module + ELM_SAVE_MODULE_SCOPE; + makeElmTagSettingScope(auxil, $1, $1s, K_MODULE, ELM_ROLE_IMPORTED); + } (_1_ 'exposing' _0_ '(' _0_ importedList _0_ ')')? EOS { + ELM_RESTORE_MODULE_SCOPE; + } + +importedList <- importedItem _0_ (',' _0_ importedList)* + +importedItem <- + importedFunction + / importedType + / importedItemIgnored + +importedFunction <- { + makeElmTag(auxil, $1, $1s, K_FUNCTION, ELM_ROLE_IMPORTED); + } + +# When importing a type and constructors we want the constructors +# to be in the scope of the type. So we have to set the scope as the +# type first, before parsing (and making the tags for) the constructors. +# That's why the code here uses two separate C code blocks. + +importedType <- + { + makeElmTagSettingScope(auxil, $1, $1s, K_TYPE, ELM_ROLE_IMPORTED); + } (_0_ '(' _0_ importedTypeConstructorList _0_ ')')? { + // We're done with the type and its constructors, so we can pop it + POP_SCOPE(auxil); + } + +importedItemIgnored <- '.'+ + +importedTypeConstructorList <- + (importedTypeConstructor / importedItemIgnored) _0_ (',' _0_ importedTypeConstructorList)* + +importedTypeConstructor <- + { + makeElmTag(auxil, $1, $1s, K_CONSTRUCTOR, ELM_ROLE_IMPORTED); + } + +# Function with a type annotation. +# +# The type is on one line, and the function must follow immediately as +# the next top level statement + +functionWithTypeAnnotation <- + _0_ ':' _0_ TLSS + <$1> _1_ ? { + int r = makeElmTagSettingScope(auxil, $3, $3s, K_FUNCTION, ROLE_DEFINITION_INDEX); + addElmTypeRef(r, $2); + addElmSignature(r, $4); + } _0_ '=' _0_ expression EOS { + POP_SCOPE(auxil); + } + +typeAnnotation <- + singleTypeSpec (_0_ '->' _0_ singleTypeSpec)* + +# Function without a type annotation + +functionDefinition <- + _0_ ? { + int r = makeElmTagSettingScope(auxil, $1, $1s, K_FUNCTION, ROLE_DEFINITION_INDEX); + addElmSignature(r, $2); + } _0_ '=' _0_ expression EOS { + POP_SCOPE(auxil); + } + +# A function parameter list is what we define a function with. It's the +# x y z in 'fn x y z'. But of course they can be more complex, such as +# 'fn (Cons a b) ({ thing } as otherThing))' etc. + +functionParameterList <- functionParameter (_0_ functionParameter)* + +functionParameter <- + plainFunctionParameter + / tupleFunctionParameter + / recordFunctionParameter + / constructorFunctionParameter + +plainFunctionParameter <- + lowerStartIdentifier (_0_ asClause)? + +tupleFunctionParameter <- + '(' _0_ functionParameter (_0_ ',' _0_ functionParameter)* _0_ ')' (_0_ asClause)? + +recordFunctionParameter <- + '{' _0_ lowerStartIdentifier (_0_ ',' _0_ lowerStartIdentifier)* _0_ '}' (_0_ asClause)? + +constructorFunctionParameter <- + upperStartIdentifier (_0_ functionParameter)* (_0_ asClause)? + +asClause <- + 'as' _1_ lowerStartIdentifier + +# Expressions + +expression <- + (letInBlock _NL_IND_)? simpleExpression (_0_ binaryOperator _0_ expression)* + +simpleExpression <- + hexNumber + / decimal + / multilineString + / characterLiteral + / oneLineString + / tupleExpression + / listExpression + / recordExpression + / caseStatement + / ifThenElseStatement + / anonymousFunction + / functionCall + +tupleExpression <- + '(' _0_ expression (_0_ ',' _0_ expression)* _0_ ')' + / '(' _0_ ')' + +listExpression <- + '[' _0_ expression (_0_ ',' _0_ expression)* _0_ ']' + / '[' _0_ ']' + +recordExpression <- + '{' _0_ + (lowerStartIdentifier _0_ '|' _0_)? + recordExpressionAssignment (_0_ ',' _0_ recordExpressionAssignment)* _0_ + '}' + / '{' _0_ '}' + +recordExpressionAssignment <- + lowerStartIdentifier _0_ '=' _0_ expression + +anonymousFunction <- + '\\' _0_ functionParameterList _0_ '->' _0_ expression + +functionCall <- + ( dottedIdentifier + / '.' lowerStartIdentifier + / '(' binaryOperator ')' + ) (_1_ expression)* + +# Let/in block +# +# We'll treat let/in blocks very simply - we'll consider each line +# and expect the whole line either to be the start of a function +# definition (perhaps with some of its body) or its body. So something +# like 'f x y =' will have to be on one line. + +letInBlock <- + 'let' _NL_IND_ + letInLine (_NL_IND_ letInLine)* _NL_IND_ + 'in' + +letInLine <- + letInFunctionDefinition + / letInBlock + / letInFunctionBody + +letInFunctionDefinition <- + WS* ? WS* '=' Non_NL* { + int r = makeElmTag(auxil, $1, $1s, K_FUNCTION, ROLE_DEFINITION_INDEX); + addElmSignature(r, $2); + } + +letInFunctionParameters <- + nonKeywordIdentifier (WS+ nonKeywordIdentifier)* + +letInFunctionBody <- + !('let' / 'in') Non_NL+ + +# Case statements +# +# We're going to be pretty loose with case statements, otherwise we'd +# have to follow Elm's indentation rules. So we'll just say +# the body of a case statement is a series of patterns like this: +# -> . The might well swallow +# up a bit of the next case pattern (because to do otherwise requires +# following Elm's indentation rules), so that's why we just specify +# . + +caseStatement <- + 'case' _1_ expression _0_ 'of' _1_ + caseClauseList + +caseClauseList <- + caseClause (_1_ caseClause)* + +caseClause <- + roughCasePatternChar* '->' _0_ expression + +roughCasePatternChar <- + !('->' / TLSS / lineComment / delimitedComment / NL) . + +# If/then/else statements + +ifThenElseStatement <- + 'if' _1_ expression _1_ + 'then' _1_ expression _1_ + 'else' _1_ expression + +# Binary operators + +binaryOperator <- + '>>' / '<<' / '|>' / '<|' + / '//' / '++' / '::' + / '==' / '/=' + / '&&' / '||' + / '<=' / '>=' + / '<' / '>' + / '+' / '-' / '*' / '/' / '^' + +# Sometimes we just need to ignore the rest of the (top level) statement + +ignoreRestOfStatement <- + (multilineString / Non_WS_or_NL+) (_1_ ignoreRestOfStatement)* + +multilineString <- + '"""' (!'"""' .)* '"""' + +# Low level tokens ------------------------------------------------------- + +# Identifiers + +naiveIdentifier <- [A-Za-z_] alphanumeric* + +upperStartIdentifier <- [A-Z] alphanumeric* + +lowerStartIdentifier <- !keyword [a-z_] alphanumeric* + +alphanumeric <- [A-Za-z0-9_] + +nonKeywordIdentifier <- + !keyword naiveIdentifier + +keyword <- + 'type' !alphanumeric + / 'module' !alphanumeric + / 'port' !alphanumeric + / 'alias' !alphanumeric + / 'as' !alphanumeric + / 'exposing' !alphanumeric + / 'import' !alphanumeric + / 'let' !alphanumeric + / 'in' !alphanumeric + / 'case' !alphanumeric + / 'of' !alphanumeric + / 'if' !alphanumeric + / 'then' !alphanumeric + / 'else' !alphanumeric + +dottedIdentifier <- nonKeywordIdentifier ('.' nonKeywordIdentifier)* + +# Numbers + +decimal <- + exponentialDecimal + / simpleDecimal + +exponentialDecimal <- + simpleDecimal 'e' simpleInteger + +simpleDecimal <- + simpleInteger ('.' digits)? + / '.' digits+ + +simpleInteger <- [-+]? digits + +digits <- [0-9]+ + +hexNumber <- '0x' [0-9A-Fa-f]+ + +# One line strings and characters + +oneLineString <- '"' inStringChar* '"' + +characterLiteral <- "'" inStringChar "'" + +inStringChar <- + !('"' / NL) + ( inStringUnicodeChar / inStringEscapedChar / inStringPlainChar ) + +inStringPlainChar <- + !('"' / '\\' / NL) . + +inStringEscapedChar <- + '\\' !('u' / NL) . + +inStringUnicodeChar <- + '\\u{' [0-9A-Fa-f]+ '}' + +# Ignorable things ------------------------------------------------------- + +# Simple things... + +WS <- [ \t]+ +NL <- '\n' / '\f' / '\r' '\n'? +Non_NL <- [^\n\r\f] +Non_WS_or_NL <- [^ \t\n\r\f] +EOF <- !. + +# A delimited comment is effectively "nothing", even if it spans several +# lines. But it does separate two tokens. +# +# A line comment can only come at the end of a line. Notice here it doesn't +# include the actual newline. + +delimitedComment <- '{-' (delimitedComment / !'-}' .)* '-}' + +lineComment <- '--' Non_NL* + +# Elm whitespacing is a bit special... +# - Two statements are at the same level (eg at the top level, or statements +# in the same let...in block) only if they begin with the same indentation. +# - One line has more indentation than the previous line then it is a +# continuation of that previous line. +# - But sometimes several statements can appear on the same line if tokens +# make it obvious. Eg this is okay: +# Eg: 'myFunc = let f x y = x + y in f 3 4' +# +# We'll only worry about top level statements for this part. But we still +# need to know +# - when a top level statement begins; and +# - when two sequential tokens are part of the same top level statement. +# They may be separated by a combination of whitespace, comments, and +# newlines, but if there is a newline then that will always be followed +# by an indent. +# +# When considering how one token relates to the next in top level statements +# we should only need three kinds of "join"s: +# - Where we need whitespace, such as 'import MyModule', but that space +# may occur over multiple lines. If it's over multiple lines, the +# second token needs to be somewhat in from the first column of text. +# We'll call this _1_ - ie at least one space. +# - Where we don't need whitespace, such as 'f = 3', but that space +# may occur over multiple lines. If it's over multiple lines then again +# the second token needs to be somewhat in from the first column of text. +# We'll call this _0_ - ie possibly zero space. +# - When we've got an end of statement, and the next token is some +# meaningful code (not a comment) and starts in the first column of text. +# Then that next token is the start of the next top level statement. +# We'll call this TLSS, for top level statement separator. +# +# We can define _1_ as +# - The longest possible sequence of whitespace, delimited comments, +# newlines, and line comments, as long as it ends with a whitespace +# or a delimited comment, because then it won't be in the first column. +# +# We can define _0_ as +# - _1_ or the empty string. +# +# We can define TLSS as +# - The longest possible sequence of whitespace, delimited comments, +# newlines, and line comments, as long as it ends with a newline or EOF +# (and there's no more ignorable characters after that). +# +# PEG parsing tip: If we want to define a sequence like 'the longest +# sequence of As, Bs and Cs, as long as it ends with C' we define a short +# sequence like 'the longest sequence of As and Bs, then a C' and then +# define 'the longest sequence of those'. + +_1_short <- + (lineComment / NL)* (WS / delimitedComment) + +_1_ <- _1_short+ + + +_0_ <- _1_ / '' + +TLSS_short <- + (WS / lineComment / delimitedComment)* (NL / EOF) + +TLSS <- + TLSS_short+ + !(WS / lineComment / delimitedComment) + +# An end of statement marks the end of a top level statement, but +# doesn't consume anything + +EOS <- &( TLSS / EOF ) + +# When considering lines in a let/in block we'll want to look for +# a newline and an indent. There may be some delimited comments etc +# in between. + +_NL_IND_ <- + TLSS_short+ WS+ + +%% +#include "elm_post.h" diff --git a/peg/elm_post.h b/peg/elm_post.h new file mode 100644 index 0000000000..d425573071 --- /dev/null +++ b/peg/elm_post.h @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2022 Nik Silver + * based on the Kotlin code by Jan DolinĂ¡r + * + * This source code is released for free distribution under the terms of the + * GNU General Public License version 2 or (at your option) any later version. + * + * This module contains functions to generate tags for Elm. + * See https://elm-lang.org/docs/syntax for language reference. + */ + +/* + * INCLUDE FILES + */ +#include "parse.h" +#include "trace.h" + +/* + * FUNCTION DEFINITIONS + */ + +/* kind - The kind of this tag. + * role - The role this tag plays. But def(ined) is expressed by setting + * this to ROLE_DEFINITION_INDEX. + * pushScope - If true, also update the scope to be this tag. + */ +static int makeElmTag (struct parserCtx *auxil, const char *name, long offset, int kind, int role) +{ + tagEntryInfo e; + + if (role == ROLE_DEFINITION_INDEX) + { + initTagEntry (&e, name, kind); + } + else + { + initRefTagEntry (&e, name, kind, role); + } + + e.lineNumber = getInputLineNumberForFileOffset (offset); + e.filePosition = getInputFilePositionForLine (e.lineNumber); + e.extensionFields.scopeIndex = BASE_SCOPE (auxil); + int scope_index = makeTagEntry (&e); + return scope_index; +} + +/* Like makeElmTag, but have this tag be the latest scope. + */ +static int makeElmTagSettingScope (struct parserCtx *auxil, const char *name, long offset, int kind, int role) +{ + // Here is a useless line just to use a function and so + // get rid of a compiler warning. + if (1 < 0) + { + PEEK_KIND (auxil); + } + // The real function starts here... + + int scope_index = makeElmTag (auxil, name, offset, kind, role); + SET_SCOPE (auxil, scope_index); + return scope_index; +} + +static void addElmSignature(int scope_index, const char *sig) +{ + tagEntryInfo *e = getEntryInCorkQueue (scope_index); + + if (e) + { + vString *vsig = collapseWhitespace (sig); + + e->extensionFields.signature = vStringDeleteUnwrap (vsig); + } +} + +static void addElmTypeRef(int scope_index, const char *sig) +{ + tagEntryInfo *e = getEntryInCorkQueue (scope_index); + + if (e) + { + vString *vsig = collapseWhitespace (sig); + + e->extensionFields.typeRef [0] = eStrdup ("typename"); + e->extensionFields.typeRef [1] = vStringDeleteUnwrap (vsig); + } +} + +/* There are several steps to making the type of constructors within + * a custom type: + * 1. Initialise the fields when we encounter the custom type declaration. + * 2. Initialise the subtype field (do this for each new constructor). + * 3. Add each subtype as we find it. + * 4. Make the typeref field. + * 5. Tidy up + */ +static void initElmConstructorFields (struct parserCtx *auxil, const char *name) +{ + auxil->customType = vStringNewInit (name); + auxil->consSubtype = vStringNew (); +} + +static void initElmConstructorSubtypeFields (struct parserCtx *auxil) +{ + vStringClear (auxil->consSubtype); +} + +static void addElmConstructorSubtype (struct parserCtx *auxil, const char *name) +{ + vStringCatS (auxil->consSubtype, name); + vStringCatS (auxil->consSubtype, " -> "); +} + +static void addElmConstructorTypeRef (struct parserCtx *auxil, int tag_index) +{ + vStringCat (auxil->consSubtype, auxil->customType); + addElmTypeRef (tag_index, vStringValue (auxil->consSubtype)); +} + +static void tidyElmConstructorFields (struct parserCtx *auxil) +{ + vStringDelete (auxil->consSubtype); + vStringDelete (auxil->customType); +} + +/* For a signature such as "a1 b2 c3" we want to transform it + * to "a1 b2 c3" for the signature field. + */ +static vString *collapseWhitespace (const char *sig) +{ + vString *vstr = vStringNew (); + + const char *c = sig; + char c2; + int found_ws = 0; + + for ( ; *c != '\0'; c++) + { + // The character, in case we need to change it + c2 = *c; + + if (isspace ((unsigned char) c2)) + { + // It's whitespace. Make it plain space + c2 = ' '; + if (found_ws) + { + // We found whitespace just before, so ignore this + continue; + } + + found_ws = 1; + } + else + { + found_ws = 0; + } + vStringPut (vstr, c2); + } + + return vstr; +} + +static void ctxInit (struct parserCtx *auxil) +{ + BASE_INIT (auxil, K_MODULE); +} + +static void ctxFini (struct parserCtx *auxil) +{ + BASE_FINI (auxil); +} + +static void findElmTags (void) +{ + struct parserCtx auxil; + + ctxInit (&auxil); + pelm_context_t *pctx = pelm_create (&auxil); + + while (pelm_parse (pctx, NULL) && (! BASE_ERROR (&auxil))) + ; + + pelm_destroy (pctx); + ctxFini (&auxil); +} + +extern parserDefinition *ElmParser (void) +{ + static const char *const extensions [] = { "elm", NULL }; + parserDefinition *def = parserNew ("Elm"); + def->kindTable = ElmKinds; + def->kindCount = ARRAY_SIZE (ElmKinds); + def->extensions = extensions; + def->parser = findElmTags; + def->fieldTable = ElmFields; + def->fieldCount = ARRAY_SIZE (ElmFields); + def->useCork = true; + def->enabled = true; + //def->requestAutomaticFQTag = true; + def->defaultScopeSeparator = "."; + return def; +} diff --git a/peg/elm_pre.h b/peg/elm_pre.h new file mode 100644 index 0000000000..eaaeac834f --- /dev/null +++ b/peg/elm_pre.h @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2022 Nik Silver + * + * This source code is released for free distribution under the terms of the + * GNU General Public License version 2 or (at your option) any later version. + * + * This module contains macros, data decls and prototypes to generate tags for Elm. + */ + +/* + * INCLUDE FILES + */ + +#include "kind.h" +#include "peg_common.h" + +/* + * This allows us to save and restore the module scope. + * We want to do this because it's unhelpful to say that an imported item + * is the scope of the importing module. + */ + +// Remember the scope of the module tag. But at first there is none (CORK_NIL). +static int elm_module_scope_index; + +#define ELM_INIT_MODULE_SCOPE \ + elm_module_scope_index = CORK_NIL +#define ELM_SAVE_MODULE_SCOPE \ + if (elm_module_scope_index != CORK_NIL) { \ + POP_SCOPE (auxil); \ + } +#define ELM_RESTORE_MODULE_SCOPE \ + if (elm_module_scope_index != CORK_NIL) { \ + SET_SCOPE (auxil, elm_module_scope_index); \ + } else { \ + POP_SCOPE (auxil); \ + } + +/* + * DATA DECLARATIONS + */ +typedef enum { + K_MODULE, + K_NAMESPACE, + K_TYPE, + K_CONSTRUCTOR, + K_ALIAS, + K_PORT, + K_FUNCTION, + COUNT_KINDS, +} ElmKind; + +/* We only define roles which aren't def(ined) + */ +typedef enum { + ELM_ROLE_IMPORTED +} elmRoles; + +static roleDefinition ElmRoles [] = { + { true, "imported", "item imported" }, +}; + +typedef enum { + F_MODULENAME, + COUNT_FIELDS, +} ElmField; + +static fieldDefinition ElmFields [COUNT_FIELDS] = { + { .name = "moduleName", + .description = "actual name of renamed module", + .enabled = true }, +}; + +/* Use referenceOnly = true when a tag must always appear + * as role that's not def(ined). + */ +static kindDefinition ElmKinds [COUNT_KINDS] = { + { true, 'm', "module", "modules", + .referenceOnly = false, ATTACH_ROLES (ElmRoles) }, + { true, 'n', "namespace", "modules renamed", }, + { true, 't', "type", "types", + .referenceOnly = false, ATTACH_ROLES (ElmRoles) }, + { true, 'c', "constructor", "constructors", + .referenceOnly = false, ATTACH_ROLES (ElmRoles) }, + { true, 'a', "alias", "aliases", }, + { true, 'p', "port", "ports", }, + { true, 'f', "function", "functions", + .referenceOnly = false, ATTACH_ROLES (ElmRoles) }, +}; + +struct parserCtx { + struct parserBaseCtx base; + vString *customType; + vString *consSubtype; +}; + +/* +* FUNCTION PROTOTYPES +*/ +#define USE_KIND_STACK KIND_GHOST_INDEX +static int makeElmTag (struct parserCtx *auxil, const char *name, long offset, int kind, int role); +static int makeElmTagSettingScope (struct parserCtx *auxil, const char *name, long offset, int kind, int role); +static void addElmSignature(int scope_index, const char *sig); +static void addElmTypeRef(int scope_index, const char *str); +static void initElmConstructorFields (struct parserCtx *auxil, const char *name); +static void initElmConstructorSubtypeFields (struct parserCtx *auxil); +static void addElmConstructorSubtype (struct parserCtx *auxil, const char *name); +static void addElmConstructorTypeRef (struct parserCtx *auxil, int tag_index); +static void tidyElmConstructorFields (struct parserCtx *auxil); +static vString *collapseWhitespace (const char *sig); diff --git a/source.mak b/source.mak index 18b7459290..5387e89e73 100644 --- a/source.mak +++ b/source.mak @@ -204,7 +204,6 @@ OPTLIB2C_INPUT = \ optlib/cmake.ctags \ optlib/ctags-optlib.ctags \ optlib/elixir.ctags \ - optlib/elm.ctags \ optlib/gdbinit.ctags \ optlib/inko.ctags \ optlib/iPythonCell.ctags \ @@ -236,6 +235,7 @@ PEG_INPUT = \ peg/varlink.peg \ peg/kotlin.peg \ peg/thrift.peg \ + peg/elm.peg \ \ $(NULL) PEG_SRCS = $(PEG_INPUT:.peg=.c) diff --git a/win32/ctags_vs2013.vcxproj b/win32/ctags_vs2013.vcxproj index 5bfb6e84b8..aac34cb094 100644 --- a/win32/ctags_vs2013.vcxproj +++ b/win32/ctags_vs2013.vcxproj @@ -233,7 +233,6 @@ - diff --git a/win32/ctags_vs2013.vcxproj.filters b/win32/ctags_vs2013.vcxproj.filters index 33ffcb3f2e..5351df2e57 100644 --- a/win32/ctags_vs2013.vcxproj.filters +++ b/win32/ctags_vs2013.vcxproj.filters @@ -222,9 +222,6 @@ Source Files\optlib - - Source Files\optlib - Source Files\optlib