Skip to content
This repository was archived by the owner on Sep 10, 2025. It is now read-only.

Added a lexer/parser/evaluator - rather than parsing via strings.Split#3

Merged
skx merged 23 commits intomasterfrom
02-lexer-parser
Sep 6, 2018
Merged

Added a lexer/parser/evaluator - rather than parsing via strings.Split#3
skx merged 23 commits intomasterfrom
02-lexer-parser

Conversation

@skx
Copy link
Owner

@skx skx commented Sep 5, 2018

This pull-request updates the codebase to make it more robust, although it is still work-in-progress.

The intention is:

  • We add a notion of suitable tokens.
  • We produce a stream of tokens from our input-file via a lexer.
  • A parser will use the tokens from the lexer and produce a series of statements.
  • The main driver will interpret that series of statements.

We probably don't need an AST or full evaluation environment, because we've zero control-flow related primitives (if, do/while, for, goto, etc). It might be that in the future we'll add some simple built-in functions for fetching github-release locations, etc, but I think that's not really required.

To test the code I'll also move towards using sub-commands appropriately:

   deployr lex input/file.here
   deployr parse input/file.here
   deployr run input/file.here

skx added 14 commits September 5, 2018 17:42
The lexer processes input from a string, and returns a series of
tokens:

* Single-line comments, beginning with "#" are skipped.
  * So is any shebang line - this is redundent.
* Whitespace is swallowed and strings are handled appropriately.
  * The following all work as expected:
    * "\t"
    * "\n"
    * "Embedded quote: \"."
This covers:

* The lexing.
* The reason for why we can't just evaluate the series of tokens
  from the lexer directly.

TODO:

* Parse the damn tokens into statements, and then execute them.
This commit removes our implementation; at this point we have
a lexer that appears to work - at least the test-cases pass - but
nothing using it.

TODO:

* Add a some sub-command stub to test the lexer.
Updated `HACKING.md` to describe how I'm going to parse; specifically
that we'll recycle the token-types in `token/token.go`.

Should be easy, but first tea :)
I've added the sub-command framework I'm most familiar with, and
now I can successfully lex our example recipe:

    frodo ~/go/src/github.com/skx/deployr $ ./deployr lex ./example.recipe
    {Set Set}
    {IDENT greeting}
    {STRING Hello, world!}
    {CopyTemplate CopyTemplate}
    {IDENT example.recipe.template}
    {IDENT /tmp/blah}
    {Run Run}
    {STRING cat /tmp/blah}
    {IfChanged IfChanged}
    {STRING sha1sum /tmp/blah}
    {EOF }

TODO:

* Write the parser.
* Test the parser.
* Use the parser.
Random-testing of our lexer to dump broken-programs revealed
the fact that we silently closed open-strings at EOF.  This is
friendly and helpful, but wrong.

Correctly return an error for this input:

        Run "test

Error is "Unterminated string!" which is nice and descriptive.
The parser is driven by the new `parse` sub-command, which shows
parsing good/bad programs like so:

    frodo ~/go/src/github.com/skx/deployr $ ./deployr parse ./example.recipe
    {{Set Set} [{IDENT greeting} {STRING Hello, world!}]}
    {{CopyTemplate CopyTemplate} [{IDENT example.recipe.template} {IDENT /tmp/blah}]}
    {{Run Run} [{STRING cat /tmp/blah}]}
    {{IfChanged IfChanged} [{STRING sha1sum /tmp/blah}]}

    frodo ~/go/src/github.com/skx/deployr $ ./deployr parse ./t.in
    Error parsing program: Error retrieceved from the lexer - Unterminated string!

As expected the parsed-results are just an array of "Statements" where
statements hold a single token as the thing to invoke, and an array
of (up to) two potential arguments.
This catches the case of:

        echo '"Test"' > t.in
        ./deployr parse t.in
We can now run our example!  First run shows both commands
work, as expected:

    frodo ~/go/src/github.com/skx/deployr $ ./deployr run --target=master.steve.org.uk:2222 ./example.recipe
    This file was deployed to master.steve.org.uk by deployr!

    Here you see a variable being used which was defined in the recipe itself:

    * Hello, world!
    5649664271eb6d14e7073ca98cf1dffa4ec1e5d0  /tmp/blah

Second run results in no change, so the "IfChanged" execution
doens't run:

    frodo ~/go/src/github.com/skx/deployr $ ./deployr run --target=master.steve.org.uk:2222 ./example.recipe
    This file was deployed to master.steve.org.uk by deployr!

    Here you see a variable being used which was defined in the recipe itself:

    * Hello, world!
Expand "${foo}" to the content of the variable "foo", in all
our functions.
This should be "Run foo .. bar":

        Run "foo \
   bar";

If you want a newline you should add one.  This might surprise
coders, but if we pretend we're bash-scripts it makes more sense.
@skx
Copy link
Owner Author

skx commented Sep 6, 2018

I believe this is ready now! Some epic-changes, which have really rewritten the application from scratch:

  • We have a lexer.
  • We have a parser.
  • We have an evaluator.

We have some tests, but need more, and my existing recipes work, however there are some breaking changes:

  • Variables being set must be quoted.
  • As must arguments to Run and IfChanged.

For example here's a diff:

   frodo ~/Repos/git.steve.org.uk/server/deployr/overseer $ git diff .
   diff --git a/overseer/deploy.recipe b/overseer/deploy.recipe
   index e0404e8..739c559 100644
   --- a/overseer/deploy.recipe
   +++ b/overseer/deploy.recipe
   @@ -12,67 +12,67 @@ DeployTo root@alert.steve.fi:2222
    #
    # Set the release version as a variable named "RELEASE".
    #
   -Set RELEASE 1.6
   +Set RELEASE "1.6"

    #
    # The path where we deploy applications.
    #
   -Set BIN /opt/overseer/bin
   +Set BIN "/opt/overseer/bin"
    
    #
    # Ensure we have a destination directory
    #
   -Run mkdir -p /opt/overseer/bin >/dev/null
   +Run "mkdir -p /opt/overseer/bin >/dev/null"

@skx skx mentioned this pull request Sep 6, 2018
@skx
Copy link
Owner Author

skx commented Sep 6, 2018

TODO:

  • Write tests for the parser.
  • Add a nop-mode to the evaluator
    • Such that I can then write tests for that.
    • While I do want this, it's an unrelated feature to this issue.

@skx skx changed the title Added a simple lexer. Added a lexer/parser/evaluator - rather than parsing via strings.Split Sep 6, 2018
skx added 8 commits September 6, 2018 07:32
Thanks "go vet" :)
Also moved variable-discussion into its own section.
Now we can add a test for it.
This is a good thing :)
This will allow us to mock our lexer for tests.
@skx
Copy link
Owner Author

skx commented Sep 6, 2018

If I can get my 100% test coverage of the parser then I'll merge this. Spent the past few hours asleep, and I'm feeling optimistic :)

@skx skx merged commit 2bd31ee into master Sep 6, 2018
@skx skx deleted the 02-lexer-parser branch September 7, 2018 16:37
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant