Patch title: Release 85 bulk changes
Abstract:
File: /pliant/language/parser/extend.page
Key:
    Removed line
    Added line
title "Extending or switching Pliant syntax"


header "simply extending the default Pliant syntax"

  header "defining new operators"

  
    [If you just wish to add a few operators to the default Pliant syntax to be able to write your programs in a more convenient way, you may use the 'operator' function defined in the ] ; link "extend.pli" "extend.pli" ; [ module to do it very simply.]

  
    para
      [It's prototype is:]
      listing
        function operator `name `priority `nb_before `nb_after
          arg Int `priority `nb_before `nb_after

  
    para
      [Operators are handled from the highest priority first down to the lowest priority. At equal priority, the left one is handled first. You'll find the priority of the predefined operators in the ] ; link "presentation of the default Pliant syntax" "default_syntax"

  
    para
      [The 'nb_before' and 'nb_after' parameters specify how many tokens the operator is expected to find before it, and after it. Most operators are binary operators that expect one token before and one after.]

    para
      [You can find an example defining the operator '.and.' which is the bit by bit and operator in the ] ; link "int.pli" "../type/number/int.pli" ; [ module.[lf]]
      [It is very simple since it's only one line:]
      listing
        operator '.and.' 260h 1 1

    para
      [The remaining of this document may be hard to read, so if you wish to customize very deeply the Pliant syntax and get troubles due to the poor quality of this document, please email your questions to ] ; link "Hubert Tonneau" "mailto:hubert.tonneau@pliant.cx" ; [; i'll be happy to help you and improve this document in the same time provided you agree to send me the source code of your module at the end and allow me to take it as an example of deep extension.]

  header "defining new parser filters"

    [A parser filter is responsible of scanning the source code in order to detect a given kind of object and build the corresponding object.]

    para
      [The prototype of a parser filter function is:]
      listing
        function `my_function `context `line `parameter
          arg ParserContext `context ; arg Str `line ; arg address `parameter
      ['context' is the data structure that stores the current state of the parsing process.[lf]]
      ['line' parameter is a string that maps the remaining characters on the current line being parsed.[lf]]
      ['parameter' is the address stored in the 'parameter' field of the ParserFilter object which introduces the new operator]

    para
      [In order to record a parser filter function, you have to create a ParserFilter object.]

   para
     [So the whole code for recording the parser filter function will look like:]
     listing
       function my_function context line parameter
         var Pointer:MyType my_parameter :> parameter map MyType
         ...
       var MyType my_parameter := ...
       var ParserFilter my_filter
       my_filter function :> fun my_function ParserContext Str address
       my_filter parameter := addressof my_parameter
       current_module define "pliant parser basic types" false addressof:my_filter

    para
      [You can find an example of a new filter in ]
      link "int.pli" "../type/number/int.pli"
      [ where the function 'parse_bin' is a filter that recognize integers in binary notation such as ] ; fixed [ 01101110b ] ; [.]


header "Overview of the low level parser engine"

  [It is still possible to get deeper in the Pliant parsing machinery in order to make more important changes. The rest of this chapter try to give you some guidelines on how it works. Anyway you should first read the end of this chapter, then study the details of the ] ; link "engine.c" "engine.c" ; [ source code.]

  para
    [The general task of the parser is to turn the source code to a tree of expressions, then call the compiler to convert that tree to an executable.]

  para
    [The Pliant parser is made of two stages:]
    list
      item [the parsing engine which is the framework of the parser. It's job is not to truly parse the source code but rather to call the parsing functions in a well defined order so that they do the true parsing correctly.]
      item [the parsing functions which do the true parsing. There is a set of default ones written in C, and the user can add or change some in order to get a richer of different syntax.]


header "How the low level parsing engine truly works"

  header "main parser engine function"

    [The main function of the Pliant engine is 'compile_text'.[lf]]
    [In input, it takes:]
    list
      item [an List (text) which is a list of strings, each string storing one line of the source code]
      item [a Module (module) which will be used mainly to define in which environment the source code will be compiled (remind that Pliant language is not a single language but rather a ] ; link "language framework" "../../welcome/whatisit" ; [, so the syntax and semantic of each module of an application may be quite different), and more over to hold the name of the source file.]

    ['compile_text' will first create a ParserContext temporary structure that will store the status of the parsing process until the full text as been parsed, then will be dropped.[lf]]
    [As you can see in the C source code, 'compile_text' mainly loops on 'ParserContext_parse_one_token' until the end of the source code is reached.]

  header "ordering the parser filters"

    ['ParserContext_parse_one_token' is responsible for parsing a single token of the source code. In order to do so, it will call in order each of the filters until one moves the parsing cursor forward which means that is successfull parsed a token.]

    para
      [Calling the parser filters in the right order is very important, it is the main job of 'ParserContext_parse_one_token' that uses a two stages scheme:]
      list
        item ['ParserContext_parse_one_token' scans the general dictionary for the 'pliant parser sections' symbol which is expected to be a list of strings. (Please notice that we use 'Module_first' instead of 'Dictionary_first' to find the list so that we don't see the symbols that are not visible from the current module. This allow to have several parsers in the same application.)]
        item [Each string in the list is the name of a symbol that will be also searched in the main dictionary through 'Module_first'. We expect to find some ParserFilter structures there which have to fields: a pointer to the parser filter function and an arrow to a parameter that will be passed to it as an extra parameter.]

    para
      [Nb: We could do all this with a single stage scheme since the many definitions of a single symbol in a dictionary are fully ordered, but then it will be hard to insert new symbols properly since an overdefinition of an existing symbol is always inserted in the dictionary before or behind all other definitions of that symbol.]


header "recognizing tokens: parser filters"

  header "true parser filters"

    [already covered]

  header "pseudo parser filters"

    para
      [There are also pseudo parser filters that dont really scan the source code for tokens but rather check if the parsing status has riched some point where it is time to perform other operations. As an example, 'parser_filter_execute' checks if we have parsed a full expression and if so asks to compile and execute it trough calling 'Expression_execute'.]

    para
      [Another pseudo parser filter is 'parser_filter_handle_pendings'. At some point of the source code, a true parser filter may recognize a pattern that requires to perform some extra operations when another point further in the source code will be riched. In order to do so, the true parser filter will call 'ParserContext_add_pending' that will record the function to be called and when it must be called, then 'parser_filter_handle_pendings' will do the call at the right time. The best example of it is in 'parser_filter_newline' that recognize indentation. When the next line is indented further than the current one, it records where the '{' sign must be inserted and where the corresponding '}' sign must be inserted.]

      
header "Folding expressions: parser operators"

  para
    [When a token is recognized by a true parser filter, it calls 'ParserContext_add_token' to record the recognized token. The 'ParserContext_add_token' function builds an atomic expression than contains the recognized token, stores it as another sun of the current expression pointed in the ParserContext structure and return the just built expression.]

  para
    [Now if the recognized token is an operator, the true parser filter is responsible for attaching a 'parseroperator' structure to the 'op' field of the newly created expression, that will define how priority the operator is (at folding time the operators are handled most priority first, and left one first in case of equal priority), which function is responsible for folding and an additional argument that will be passed to that function.]
    ['parser_operator_t1' is an example or a folding function which will fold a ParserFilterOperatorT1 operator.]

  para
    [The obvious way of folding is to call 'fold_arguments' function. (Should you get so deep in extending or customising Pliant syntax that the two kind of predefined operators below dont match your needs, then you still can call 'fold_arguments' function in your folding function written in Pliant since it is also exported from C to Pliant in ] ; link "startup.c" "../startup/startup.c"
    
  para
    [There are two basic kind of predefined operators:]
    list
      item
        [ParserFilterOperatorT1[lf]]
        [An example of such an operator is '+']
      item
        [ParserFilterOperatorT2[lf]]
        [An example of such an operator is the '(' ')' couple.]


header "Rewriting expressions at precompile time"

  [Just before compiling and executing a parsed expression through calling 'Expression_execute', the parser engine calls 'Expression_precompile_rewrite' which is defined in ] ; link "expression.c" "../compiler/expression/expression.c" ; [. This function will call itself recursively for each sub expressions.]

  para
    [furthermore, for each expression, it will scan the main directory for al 'pliant precompile rewrite' symbols that are visible in the current module. All the associated objects are expected to be functions that rewrite an expression.[lf]]
    [An example of such a function is 'parser_rewrite_bloc' that removes the bloc stage when a bloc contains a single instruction.]


header "summary of the parsing process"

  [From a more general point of view the Pliant parsing process is three stages:]
  list
    item [the token are recognized by filters]
    item [the expressions are folded by operators]
    item [the expressions are rewritten by precompile rewriters]