Skip to content

Parser Command Reference

Abe Pralle edited this page Mar 18, 2022 · 18 revisions

A .froley file should have one parser section which is comprised of any number of routines.

The Parser turns a token stream into an Abstract Syntax Tree (AST) of extended Cmd nodes.

Routines

- routine_name
  ...  # commands

- another_routine
  ...

The topmost routine is the default routine. It is called to parse a token stream by default; other Parser routines may be explicitly called if desired.

Routines cannot accept arguments or return values (use global variables if needed).

Parser routines automatically return at the end if no explicit return command is given.

Variables

count = 0
if (consume('{')) ++count
...
  • Variables are created simply by assigning a value to a name.
  • All variables are global variables (accessible to all Parser routines).
  • Variables are statically typed. The static type of a variable is implicitly determined by the type of the first value assigned it.
  • Variables can be used to pass values into or out of routines.

Built-in Variables

Name Description
content Read-only. See Creating Nodes.
disable_output Incrementing or setting to non-zero causes node creation commands to have no effect (createNull, produce NodeType(...), produceList, etc.). This allows parsing through source code for purposes other than creating an AST.

Literals

"abc"  # a String
'a'    # a Character
123    # an Integer
true   # Logical true
false  # Logical false

Strings and Characters are mostly interchangeable. The only caveat is if the initial value of a variable is a Character, it will not be able to later store a String.

Statements

Statement Description
++ Increment operator. Increment variables with ++varname or varname++.
-- Decrement operator. Decrement variables with --varname or varname--.
-> NodeType(...) -> is an alias for produce.
routine-name Call the named routine. If the routine does not produce or restart then control will return to the following line of the calling routine.
call user-routine-name Call an arbitrarily-named user routine AKA "native routine". The routine is not defined in the .froley file, but rather in the ParserCore class that Froley generates. Define the method by overriding it in the customizable Parser class which extends ParserCore.
create NodeType
create NodeType(args)
Push a new Cmd node on the internal stack and continue execution. See Creating Nodes.
createList
createList ListType
Creates a CmdList or extended CmdList node that contains all the nodes on the stack since the most recent beginList command. The resulting list is pushed back on the stack.
createNull Pushes a null reference onto the node stack.
discardList Cancels the most recent beginList. The stack is unaltered.
discardPosition Discards the most recent savePosition.
mustConsume("symbol")
mustConsume(TOKEN_TYPE)
If the next token matches the specified type, read and discard it. Otherwise throw an error.
mustConsume(@"content") If the string [content] of the next token matches the specified type, read and discard it. Otherwise throw an error.
noAction Does nothing. Useful as a placeholder single command for if & while.
print arg1 [arg2...] Prints one or more expressions without a newline.
println arg1 [arg2...] Prints one or more expressions followed by a newline.
produce NodeType
produce NodeType(args)
Executes a create NodeType(...) and then returns from the current routine.
produceNull Pushes a null reference on the stack and then returns from the current routine.
produceList
produceList ListType
Executes a createList ... and then returns from the current routine.
restorePosition Rewinds the input to the point of the most recent savePosition.
return Returns from the current routine. If execution reaches the end of a parser routine a return happens automatically.
savePosition Saves the current input position (line and column, etc.) to be either rewound to with restorePosition or else discarded with discardPosition.
syntaxError
syntaxError "message"
Throws a CompileError with either a default "unexpected input" message or a custom message.

Control Structures

Control Structure Description
if (condition) ...
elseIf (condition) ...
else ...
Single line if statement; separate multiple commands with ;.
if (condition)
  ...
elseIf (condition)
 ...
else
  ...
endIf
Multi-line line if statement.
on token‑type
onPeek token‑type
  ...
elseOn token‑type
elseOnPeek token‑type
  ...
elseOnOthers
  ...
endOn
on and elseOn consume the next token if it matches the specified type and make it the "current token". onPeek and elseOnPeek set current token to be the next token without consuming it. The current token is stored with any new Cmd node created within the on block. "on" and "onPeek" variations can be mixed & matched - for example on ... elseOnPeek ... or onPeek ... elseOn .... elseOnOthers does not consume the next token or change the current token; it is just the version of "else" for "on".
on/onPeek token‑type additional‑terms ‑> NodeType(...) on-produce is a shorthand version of on that allows a list of additional routine calls and token types to be specified after the initial token type. The on performs a matching decision on the first token type and then treats the remaining parse terms as "must-reads". For example, on '(' expression ')' -> Expression(operand) is equivalent to:

on '('
  expression
  expression
  mustConsume(')')
produce Expression(operand)
endOn
while (condition) ... Single line while loop; separate multiple commands with ;.
while (condition)
  ...
endWhile
Multi-line while loop.

Expressions

Expressions result in a value that can be used as a condition for if or while. They can also be used as standalone statements.

Operators

Operator Description
( expression ) Standard precedence operator.
+, -, *, /, ^ Add, subtract, multiply, divide, power.
==, !=, <=, >=, <, > Comparison Operators
not Logical not - if (not hasAnother) ....
and, or Logical AND and OR. if (hasAnother and nextIs(';')) ....

Functions

Expression Description
consume("symbol")
consume(TOKEN_TYPE)
Read and discard the next token if it matches the specified type. Evaluates to true on success.
consume(@"content") Read and discard the next token if its string content matches the specified value, regardless of token type. Evaluates to true on success. Only tokens with the [content] attribute have string content.
hasAnother Evaluates to true if another input token is available.
nextHasAttribute(attribute-name) Returns true if the next token has the named attribute - if (nextHasAttribute(content)) ....
nextIs("symbol")
nextIs(TOKEN_TYPE)
Evaluates to true if the next available token has the specified type.
nextIs(@"content") Evaluates to true if the string content of the next token matches the specified value regardless of token type. Only tokens with the [content] attribute have string content.
read Reads the next input token.

Creating Nodes

  • Commands create XYZ and produce XYZ will create a new extended Cmd node called XYZ and push it on the stack.
  • If arguments create XYZ(alpha,beta) are given, then XYZ is given properties alpha:Cmd and beta:Cmd. Two values are popped off the stack and assigned to beta and then alpha before the XYZ node is pushed on the stack.
  • Specific property types can be declared: create Routine(name:String,statements:Statements). Each property still pops one node off the stack, but in the case of String or Integer (etc.), that node's to->String or to->Int32 (etc.) method is called to convert the node into a value.
  • There are five built-in types: String, Real, Integer, Character, and Logical. Any other type names are assumed to be the names of extended Cmd types.
  • Node properties can be assigned the value content: on STRING -> LiteralString(value=content). When this happens, instead of popping off a node from the stack, value gets its value from the string content of the associated token. Assigning content to a property makes it a String property by default unless another built-in type is declared.
  • Finally, node properties can be assigned an arbitrary string of native code. The content of the string, as native code, is assigned to the property. For example, create LiteralLogical(value="true") initializes value with the value true, not with the string "true".

Example

From the Simple-Parser scanner example.

--------------------------------------------------------------------------------
parser
--------------------------------------------------------------------------------
- statements
  consume_eols

  beginList
    while (hasAnother)
      on "println"
        arg_list
        create Println(args)
      elseOnOthers
        expression
      endOn
      consume_eols
    endWhile
  produceList Statements

- consume_eols
  while (consume(EOL)) noAction

- arg_list
  beginList
    while (hasAnother and not nextIs(EOL))
      expression
    endWhile
  produceList

- expression
  assign

- assign [rightBinary]
  on "=" -> Assign

- add_subtract [binary]
  on "+" -> Add
  on "-" -> Subtract

- multiply_divide [binary]
  on "*" -> Multiply
  on "/" -> Divide

- power [binary]
  on "^" -> Power

- factorial [postUnary]
  on "!" -> Factorial

- negate [preUnary]
  on "-" -> Negate

- term
  on '(' expression ')': return
  on IDENTIFIER -> Access(name=content)
  on NUMBER     -> Number(value=content:Real)
  on STRING     -> LiteralString(value=content)
  syntaxError

Clone this wiki locally