Compiler/Verifying syntax: Difference between revisions

m
(→‎{{header|Go}}: Fixed various problems highlighted in the Talk Page and added some more examples to test them.)
m (→‎{{header|Wren}}: Minor tidy)
 
(32 intermediate revisions by 9 users not shown)
Line 44:
</pre>
 
 
=={{header|ALGOL W}}==
Includes the test cases from the Go sample. Note, strings are limited to 256 characters in Algol W.
<syntaxhighlight lang="algolw">begin
% verify expressions match expected syntax %
procedure stmt ( string(256) value text ) ; begin
% the parsing procedures return true if the expression matches the %
% required element, false otherwise - a parse tree is not built %
logical haveInteger, haveIdentifier, haveEof, hadError;
integer currPos, maxPos, syPos;
string(1) currCh;
string(256) srcText;
string(256) sy;
logical procedure expr ; expr_level_2;
logical procedure expr_level_2 ; begin
logical ok;
ok := expr_level_3;
while ok and have( "or" ) do ok := next and expr_level_3;
ok
end expr_level_2 ;
logical procedure expr_level_3 ; begin
logical ok;
ok := expr_level_4;
while have( "and" ) and ok do ok := next and expr_level_4;
ok
end expr_level_3 ;
logical procedure expr_level_4 ; begin
logical ok;
ok := true;
if have( "not" ) then ok := next;
if ok then ok := expr_level_5;
if ok and ( have( "=" ) or have( "<" ) ) then ok := next and expr_level_5;
ok
end expr_level_4 ;
logical procedure expr_level_5 ; begin
logical ok;
ok := expr_level_6;
while ok and ( have( "+" ) or have( "-" ) ) do ok := next and expr_level_6;
ok
end expr_level_5 ;
logical procedure expr_level_6 ; begin
logical ok;
ok := primary;
while ok and ( have( "*" ) or have( "/" ) ) do ok := next and primary;
ok
end expr_level_6 ;
logical procedure primary ;
if haveIdentifier or haveInteger or have( "true" ) or have( "false" ) then begin
void( next );
true
end
else if have( "(" ) then next and expr and mustBe( ")" )
else error( "Expecting identifier, literal or ""(""" ) ;
logical procedure addAndNextChar ; begin
if syPos = 255 then void( error( "Symbol too long" ) )
else if syPos < 255 then begin
sy( syPos // 1 ) := currCh;
syPos := syPos + 1
end if_syPos_eq_255__lt_255 ;
nextChar
end addAndNextChar ;
logical procedure next ; begin
logical ok;
haveInteger := haveIdentifier := false;
ok := skipSpaces;
sy := "";
syPos := 0;
if not ok then sy := "<eof>"
else begin
if haveDigit then begin
haveInteger := true;
while addAndNextChar and haveDigit do begin end
end
else if haveLetter then begin
while addAndNextChar and ( haveLetter or haveDigit or currCh = "_" ) do begin end;
haveIdentifier := sy not = "and" and sy not = "or" and sy not = "not" and sy not = "true" and sy not = "false"
end
else void( addAndNextChar );
end if_not_ok__ ;
ok
end next ;
logical procedure skipSpaces ; begin
logical ok;
ok := not haveEof;
while ok and currCh = " " do ok := nextChar;
ok
end skipSpaces ;
logical procedure haveLetter ; not haveEof and ( ( currCh >= "a" and currCh <= "z" )
or ( currCh >= "A" and currCh <= "Z" ) );
logical procedure haveDigit ; not haveEof and ( currCh >= "0" and currCh <= "9" );
logical procedure have ( string(12) value text ) ; text = sy;
logical procedure mustBe ( string(12) value text ) ; begin
logical ok;
ok := have( text );
if ok then void( next )
else begin
string(256) msg;
msg := "Expected:";
msg( strlen( msg ) + 2 // 12 ) := text;
void( error( msg ) )
end if_ok;
ok
end mustBe ;
logical procedure nextChar ; begin
haveEof := currPos > maxPos;
if not haveEof then begin
currCh := srcText( currPos // 1 );
currPos := currPos + 1
end if_not_haveEof ;
not haveEof
end nextChar ;
integer procedure strlen ( string(256) value text ) ; begin
integer length;
length := 255;
while length >= 0 and text( length // 1 ) = " " do length := length - 1;
length
end strlen ;
logical procedure error ( string(256) value msg ) ; begin
if not hadError then begin
% have the first error %
writeon( " error at: ", I_W := 1, currPos, "(" );
showText( sy, strlen( sy ) );
writeon( "): " );
showText( msg, strlen( msg ) );
hadError := true
end if_not_hadError ;
false
end ewrror ;
procedure showText ( string(256) value text; integer value length ) ; for c := 0 until length do writeon( text( c // 1 ) );
procedure void ( logical value b ) ; begin end void ;
% parse text and output messages indicating whether it is OK or not %
hadError := false;
sy := "<bof>";
srcText := text;
currPos := 0;
maxPos := strlen( srcText );
write();
showText( srcText, maxPos );
writeon( ": " );
if not nextChar then void( error( "Blank source text" ) )
else if not ( next and expr ) then void( error( "Expression expected" ) )
else if not haveEof then void( error( "Expected EOF after expression" ) );
if hadError then writeon( " false" )
else writeon( " true" )
end stmt ;
% test cases %
stmt( "wombat" );
stmt( "wombat or monotreme" );
stmt( "( wombat and not )" );
stmt( "wombat or not" );
stmt( "a + 1" );
stmt( "a + b < c" );
stmt( "a + b - c * d / e < f and not ( g = h )" );
stmt( "a + b - c * d / e < f and not ( g = h" );
stmt( "a = b" );
stmt( "a or b = c" );
stmt( "$" );
% test cases from Go %
stmt( "true or false = not true" );
stmt( "not true = false" );
stmt( "3 + not 5" );
stmt( "3 + (not 5)" );
stmt( "(42 + 3" );
stmt( " not 3 < 4 or (true or 3 / 4 + 8 * 5 - 5 * 2 < 56) and 4 * 3 < 12 or not true" );
stmt( " and 3 < 2" );
stmt( "not 7 < 2" );
stmt( "2 < 3 < 4" );
stmt( "2 < foobar - 3 < 4" );
stmt( "2 < foobar and 3 < 4" );
stmt( "4 * (32 - 16) + 9 = 73" );
stmt( "235 76 + 1" );
stmt( "a + b = not c and false" );
stmt( "a + b = (not c) and false" );
stmt( "a + b = (not c and false)" );
stmt( "ab_c / bd2 or < e_f7" );
stmt( "g not = h" );
stmt( "été = false" );
stmt( "i++" );
stmt( "j & k" );
stmt( "l or _m" )
end.</syntaxhighlight>
{{out}}
<pre>
wombat: true
wombat or monotreme: true
( wombat and not ): error at: 18 ()): Expecting identifier, literal or "(" false
wombat or not: error at: 13 (<eof>): Expression expected false
a + 1: true
a + b < c: true
a + b - c * d / e < f and not ( g = h ): true
a + b - c * d / e < f and not ( g = h: error at: 37 (<eof>): Expected: ) false
a = b: true
a or b = c: true
$: error at: 1 ($): Expecting identifier, literal or "(" false
true or false = not true: error at: 20 (not): Expecting identifier, literal or "(" false
not true = false: true
3 + not 5: error at: 8 (not): Expecting identifier, literal or "(" false
3 + (not 5): true
(42 + 3: error at: 7 (<eof>): Expected: ) false
not 3 < 4 or (true or 3 / 4 + 8 * 5 - 5 * 2 < 56) and 4 * 3 < 12 or not true: true
and 3 < 2: error at: 5 (and): Expecting identifier, literal or "(" false
not 7 < 2: true
2 < 3 < 4: error at: 8 (<): Expected EOF after expression false
2 < foobar - 3 < 4: error at: 17 (<): Expected EOF after expression false
2 < foobar and 3 < 4: true
4 * (32 - 16) + 9 = 73: true
235 76 + 1: error at: 7 (76): Expected EOF after expression false
a + b = not c and false: error at: 12 (not): Expecting identifier, literal or "(" false
a + b = (not c) and false: true
a + b = (not c and false): true
ab_c / bd2 or < e_f7: error at: 16 (<): Expecting identifier, literal or "(" false
g not = h: error at: 6 (not): Expected EOF after expression false
été = false: error at: 2 (Ã): Expecting identifier, literal or "(" false
i++: error at: 3 (+): Expecting identifier, literal or "(" false
j & k: error at: 4 (&): Expected EOF after expression false
l or _m: error at: 7 (_): Expecting identifier, literal or "(" false
</pre>
 
=={{header|C}}==
<syntaxhighlight lang="c">// cverifyingsyntaxrosetta.c
// http://www.rosettacode.org/wiki/Compiler/_Verifying_Syntax
 
/*
# Makefile
CFLAGS = -O3 -Wall -Wfatal-errors
all: cverifyingsyntaxrosetta
*/
 
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <setjmp.h>
 
#define AT(CHAR) ( *pos == CHAR && ++pos )
#define TEST(STR) ( strncmp( pos, STR, strlen(STR) ) == 0 \
&& ! isalnum(pos[strlen(STR)]) && pos[strlen(STR)] != '_' )
#define IS(STR) ( TEST(STR) && (pos += strlen(STR)) )
 
static char *pos; // current position in source
static char *startpos; // start of source
static jmp_buf jmpenv;
 
static int
error(char *message)
{
printf("false %s\n%*s^ %s\n", startpos, pos - startpos + 7, "", message);
longjmp( jmpenv, 1 );
}
 
static int
expr(int level)
{
while( isspace(*pos) ) ++pos; // skip white space
if( AT('(') ) // find a primary (operand)
{
if( expr(0) && ! AT(')') ) error("missing close paren");
}
else if( level <= 4 && IS("not") && expr(6) ) { }
else if( TEST("or") || TEST("and") || TEST("not") )
{
error("expected a primary, found an operator");
}
else if( isdigit(*pos) ) pos += strspn( pos, "0123456789" );
else if( isalpha(*pos) ) pos += strspn( pos, "0123456789_"
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" );
else error("expected a primary");
 
do // then look for zero or more valid following operators
{
while( isspace(*pos) ) ++pos;
}
while(
level <= 2 && IS("or") ? expr(3) :
level <= 3 && IS("and") ? expr(4) :
level <= 4 && (AT('=') || AT('<')) ? expr(5) :
level == 5 && (*pos == '=' || *pos == '<') ? error("non-associative") :
level <= 6 && (AT('+') || AT('-')) ? expr(7) :
level <= 7 && (AT('*') || AT('/')) ? expr(8) :
0 );
return 1;
}
 
static void
parse(char *source)
{
startpos = pos = source;
if( setjmp(jmpenv) ) return; // for catching errors during recursion
expr(0);
if( *pos ) error("unexpected character following valid parse");
printf(" true %s\n", source);
}
 
static char *tests[] = {
"3 + not 5",
"3 + (not 5)",
"(42 + 3",
"(42 + 3 some_other_syntax_error",
"not 3 < 4 or (true or 3/4+8*5-5*2 < 56) and 4*3 < 12 or not true",
"and 3 < 2",
"not 7 < 2",
"2 < 3 < 4",
"2 < foobar - 3 < 4",
"2 < foobar and 3 < 4",
"4 * (32 - 16) + 9 = 73",
"235 76 + 1",
"a + b = not c and false",
"a + b = (not c) and false",
"a + b = (not c and false)",
"ab_c / bd2 or < e_f7",
"g not = h",
"i++",
"j & k",
"l or _m",
"wombat",
"WOMBAT or monotreme",
"a + b - c * d / e < f and not ( g = h )",
"$",
};
 
int
main(int argc, char *argv[])
{
for( int i = 0; i < sizeof(tests)/sizeof(*tests); i++ ) parse(tests[i]);
}</syntaxhighlight>
{{out}}
<pre>
false 3 + not 5
^ expected a primary, found an operator
true 3 + (not 5)
false (42 + 3
^ missing close paren
false (42 + 3 some_other_syntax_error
^ missing close paren
true not 3 < 4 or (true or 3/4+8*5-5*2 < 56) and 4*3 < 12 or not true
false and 3 < 2
^ expected a primary, found an operator
true not 7 < 2
false 2 < 3 < 4
^ non-associative
false 2 < foobar - 3 < 4
^ non-associative
true 2 < foobar and 3 < 4
true 4 * (32 - 16) + 9 = 73
false 235 76 + 1
^ unexpected character following valid parse
false a + b = not c and false
^ expected a primary, found an operator
true a + b = (not c) and false
true a + b = (not c and false)
false ab_c / bd2 or < e_f7
^ expected a primary
false g not = h
^ unexpected character following valid parse
false i++
^ expected a primary
false j & k
^ unexpected character following valid parse
false l or _m
^ expected a primary
true wombat
true WOMBAT or monotreme
true a + b - c * d / e < f and not ( g = h )
false $
^ expected a primary
</pre>
 
=={{header|Go}}==
Line 50 ⟶ 415:
After making the appropriate substitutions, we can therefore use the parser in Go's standard library to verify whether the statements are valid or not. As expressions cannot be statements in Go, we simply parse the latter as expressions.
 
However, before applying the parser, we first need to ensure that the expressions don't include any characters (including non-ASCII) or usages thereof which Go would otherwise permit. Note that it's not necessary to specifically check for '"++'" and '"--'" as these are statements in Go and can't appear in expressions anyway.
 
In particular, after substitutions, '"= not'", "+ not" etc. would be allowed by the Go parser so we need to exclude itthem. Curiously, the Go parser allows something like '"2 < 3 < 4'" even though it doesn't compile. We need therefore to exclude that also (see Talk page).
<langsyntaxhighlight lang="go">package main
 
import (
Line 63 ⟶ 428:
 
var (
re1 = regexp.MustCompile(`[^_a-zA-Z0-9\+\-\*/=<\(\) \s]`)
re2 = regexp.MustCompile(`\b_\w*\b`)
re3 = regexp.MustCompile(`=[ =<+*/-]\s*not`)
re4 = regexp.MustCompile(`(=|<)[ ]\s*[^(=< ]+[ ]\s*([=|<+*/-])`)
)
 
Line 86 ⟶ 451:
matches = re4.FindStringSubmatch(expr)
if matches != nil {
return fmt.Errorf("expectedoperator EOF,%q foundis %qnon-associative", []rune(matches[21])[0])
}
return nil
Line 101 ⟶ 466:
func main() {
exprs := []string{
"$",
"one",
"either or both",
"a + 1",
"a + b < c",
"a = b",
"a or b = c",
"3 + not 5",
"3 + (not 5)",
"(42 + 3",
"(42 + 3)",
" not 3 < 4 or (true or 3 / 4 + 8 * 5 - 5 * 2 < 56) and 4 * 3 < 12 or not true",
" and 3 < 2",
"not 7 < 2",
"2 < 3 < 4",
"2 < (3 < 4)",
"2 < foobar - 3 < 4",
"2 < foobar and 3 < 4",
"4 * (32 - 16) + 9 = 73",
"235 76 + 1",
"true or false = not true",
"true or false = (not true)",
"not true or false = false",
"not true = false",
"a + b = not c and false",
"a + b = (not c) and false",
"a + b = (not c and false)",
"ab_c / bd2 or < e_f7",
"g not = h",
Line 116 ⟶ 499:
"j & k",
"l or _m",
}
 
for _, expr := range exprs {
fmt.Printf("Statement to verify: %q\n", expr)
Line 136 ⟶ 519:
fmt.Println()
}
}</langsyntaxhighlight>
 
{{out}}
<pre>
Statement to verify: "$"
"false" -> invalid character '$' found
 
Statement to verify: "one"
"true"
 
Statement to verify: "either or both"
"true"
 
Statement to verify: "a + 1"
"true"
 
Statement to verify: "a + b < c"
"true"
 
Statement to verify: "a = b"
"true"
 
Statement to verify: "a or b = c"
"true"
 
Statement to verify: "3 + not 5"
"false" -> expected operand, found 'not'
 
Statement to verify: "3 + (not 5)"
"true"
 
Statement to verify: "(42 + 3"
"false" -> expected ')', found newline
 
Statement to verify: "(42 + 3)"
"true"
 
Statement to verify: " not 3 < 4 or (true or 3 / 4 + 8 * 5 - 5 * 2 < 56) and 4 * 3 < 12 or not true"
Line 153 ⟶ 566:
 
Statement to verify: "2 < 3 < 4"
"false" -> expected EOF, foundoperator '<' is non-associative
 
Statement to verify: "2 < (3 < 4)"
"true"
 
Statement to verify: "2 < foobar - 3 < 4"
"false" -> operator '<' is non-associative
 
Statement to verify: "2 < foobar and 3 < 4"
"true"
 
Statement to verify: "4 * (32 - 16) + 9 = 73"
Line 160 ⟶ 582:
Statement to verify: "235 76 + 1"
"false" -> expected 'EOF', found 76
 
Statement to verify: "true or false = not true"
"false" -> expected operand, found 'not'
 
Statement to verify: "true or false = (not true)"
"true"
 
Statement to verify: "not true or false = false"
"true"
 
Statement to verify: "not true = false"
"true"
 
Statement to verify: "a + b = not c and false"
Line 165 ⟶ 599:
 
Statement to verify: "a + b = (not c) and false"
"true"
 
Statement to verify: "a + b = (not c and false)"
"true"
 
Line 185 ⟶ 622:
"false" -> identifier cannot begin with an underscore
</pre>
 
=={{header|jq}}==
{{works with|jq}}
'''Also works with gojq, the Go implementation of jq'''
 
This entry uses the PEG (Parsing Expression Grammar) formalism
to transform the given grammar to a jq verification program
by a simple process that amounts to transcription.
 
For example, in the rule for `primary`, the alternation
 
Identifier | Integer
 
becomes the jq expression:
 
Identifer // Integer
 
The transcription process is not completely trivial as
jq requires definitions be ordered and perhaps nested
to satisfy a "define-before-use" rule. In the present case,
since `primary` and `expr` are defined circularly,
we define `primary` as an inner function of `expr`.
 
This PEG-to-jq transcription process is
described in detail at
[https://github.com/stedolan/jq/wiki/Parsing-Expression-Grammars].
 
The following presentation uses jq's support for regular expressions
for the sake of simplicity, brevity and efficiency.
For example, the grammar rule:
 
Digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ;
 
becomes the jq program:
 
def Digit: parse("[0-9]");
 
where `parse` is a utility function defined in the library
of PEG-oriented functions in the first subsection below.
 
The jq program presented here works on character strings and textual files, and hence
the use of `ws` (for whitespace) in the program. Since `ws` is defined here
to make whitespace optional, it might be desirable to modify the program
to require whitespace (e.g. using the utility function `_`) in certain places instead.
</syntaxhighlight>
====Generic PEG Library====
The jq module at [[:Category:Jq/peg.jq]] can be included by copying it to a file,
and adding an `include` statement to top of the main program, e.g. as follows:
<syntaxhighlight lang=jq>
include "peg" {search: "."};
</syntaxhighlight>
 
====The Grammar====
<syntaxhighlight lang=jq>
def expr:
def Digit : parse("[0-9]");
def Letter : parse("[a-zA-Z]");
def Identifier : Letter | star(Letter // Digit // literal("_"));
def Integer : plus(Digit);
def primary : ws
| (Identifier
// Integer
// (literal("(") | expr | literal(")"))
// literal("true")
// literal("false"))
| ws;
def expr_level_6 : primary | star((literal("*") // literal("/")) | primary) ;
def expr_level_5 : expr_level_6 | star((literal("+") // literal("-")) | expr_level_6) ;
def expr_level_4 : ws | optional(literal("not")) | expr_level_5 | optional(parse("[=<]") | expr_level_5) ;
def expr_level_3 : expr_level_4 | star(literal("and") | expr_level_4) ;
def expr_level_2 : expr_level_3 | star(literal("or") | expr_level_3) ;
 
ws | expr_level_2 | ws;
 
def stmt:
{remainder: .} | expr | eos;
</syntaxhighlight>
====The Go examples====
<syntaxhighlight lang=jq>
def go: [
"$",
"one",
"either or both",
"a + 1",
"a + b < c",
"a = b",
"a or b = c",
"3 + not 5",
"3 + (not 5)",
"(42 + 3",
"(42 + 3)",
" not 3 < 4 or (true or 3 / 4 + 8 * 5 - 5 * 2 < 56) and 4 * 3 < 12 or not true",
" and 3 < 2",
"not 7 < 2",
"2 < 3 < 4",
"2 < (3 < 4)",
"2 < foobar - 3 < 4",
"2 < foobar and 3 < 4",
"4 * (32 - 16) + 9 = 73",
"235 76 + 1",
"true or false = not true",
"true or false = (not true)",
"not true or false = false",
"not true = false",
"a + b = not c and false",
"a + b = (not c) and false",
"a + b = (not c and false)",
"ab_c / bd2 or < e_f7",
"g not = h",
"été = false",
"i++",
"j & k",
"l or _m"
];
 
# For ease of comparison with the Go output, simply emit `true` or `false`
go[]
| (stmt | true) // false
</syntaxhighlight>
 
'''Invocation''': jq -nr -f compiler-verifying-syntax.jq
{{output}}
The same sequence of `true` and `false` values as at [[#Go|Go]].
 
=={{header|Julia}}==
<langsyntaxhighlight lang="julia">function substituteinnerparentheses(s, subs)
((i = findlast('(', s)) == nothing) && return (s, false)
((j = findfirst(')', s[i:end])) == nothing) && return (s, false)
Line 193 ⟶ 758:
return s[1:i-1] * " " * subs * " " * s[j+i:end], true
end
 
function okparse(s)
while findfirst('(', s) != nothing
Line 200 ⟶ 765:
end
s = strip(s)
# Julia allows expressions like 2 + + + 3, or like true = not false, but these are not allowed here
# = or < can be used only once within parentheses
if occursin(r"(and|or|[\=\<\+\-\*\/])\s*(and|or|[\=\<\+\-\*\/])", s) ||
occursin(r"(^(and|^or|^[\=\<\+\-\*\/]))|((and|or|[\=\<\+\-\*\/])$)", s) ||
occursin(r"(\=|\<)\s*not\s", s) || count(c -> c == '=' || c == '<', s) > 1
return false
end
Line 222 ⟶ 789:
return true
end
 
teststatements = [
" not 3 < 4 or (true or 3 / 4 + 8 * 5 - 5 * 2 < 56) and 4 * 3 < 12 or not true",
Line 228 ⟶ 795:
"not 7 < 2",
"4 * (32 - 16) + 9 = 73",
"235 76 + 1",
"true or false = not true",
"not true = false",
"2 < 5 < 9"
]
 
for s in teststatements
println("The compiler parses the statement { $s } and outputs: ", okparse(s))
end
</langsyntaxhighlight>{{out}}
<pre>
The compiler parses the statement { not 3 < 4 or (true or 3 / 4 + 8 * 5 - 5 * 2 < 56) and 4 * 3 < 12 or not true } and outputs: true
Line 241 ⟶ 811:
The compiler parses the statement { 4 * (32 - 16) + 9 = 73 } and outputs: true
The compiler parses the statement { 235 76 + 1 } and outputs: false
The compiler parses the statement { true or false = not true } and outputs: false
The compiler parses the statement { not true = false } and outputs: true
The compiler parses the statement { 2 < 5 < 9 } and outputs: false
</pre>
 
=={{header|Nim}}==
<syntaxhighlight lang="nim">import strutils, tables
 
type
 
# List of tokens with their textual representation.
Token = enum tkError = "invalid token", tkIdent = "identifier", tkInt = "integer",
tkLPar = "'('", tkRPar = "')'", tkFalse = "'false'", tkTrue = "'true'",
tkLt = "'<'", tkEq = "'='", tkAdd = "'+'", tkSub = "'-'", tkMul = "'*'",
tkDiv = "'/'", tkOr = "'or'", tkAnd = "'and'", tkNot = "'not'", tkEOF = "EOF"
 
Lexer = object
str: string # String to parse.
len: Natural # String length.
pos: int # Current lexer position.
token: Token # Current token.
error: string # Error message.
 
const
# Mapping of reserved identifiers to tokens.
IdentTokens = {"false": tkFalse, "true": tkTrue, "or": tkOr, "and": tkAnd, "not": tkNot}.toTable
# Mapping of characters to tokens.
CharTokens = {'(': tkLPar, ')': tkRPar, '<': tkLt, '=': tkEq,
'+': tkAdd, '-': tkSub, '*': tkMul, '/': tkDiv}.toTable
 
 
####################################################################################################
# Lexer.
 
# Forward reference.
func nextToken(lex: var Lexer)
 
 
func initLexer(str: string): Lexer =
## Initialize a lexer.
result = Lexer(str: str, len: str.len, pos: 0)
result.nextToken() # Always a token ahead.
 
 
func getIdToken(lex: var Lexer) =
## Get the token for an identifier.
var str: string
while lex.pos < lex.len and lex.str[lex.pos] in IdentChars:
str.add lex.str[lex.pos]
inc lex.pos
lex.token = IdentTokens.getOrDefault(str, tkIdent)
 
 
func getInt(lex: var Lexer) =
## Get an integer token.
while lex.pos < lex.len and lex.str[lex.pos] in Digits:
inc lex.pos
lex.token = tkInt
 
 
func nextToken(lex: var Lexer) =
## Find the next token.
 
# Skip spaces.
while lex.pos < lex.str.len and lex.str[lex.pos] == ' ':
inc lex.pos
 
if lex.pos == lex.str.len:
lex.token = tkEOF
 
else:
let ch = lex.str[lex.pos]
case ch
of 'a'..'z':
lex.getIdToken()
of '0'..'9':
lex.getint()
else:
inc lex.pos
lex.token = CharTokens.getOrDefault(ch, tkError)
 
 
####################################################################################################
# Parser.
 
# Forward reference.
proc checkExpr(lex: var Lexer): bool
 
 
proc checkPrimary(lex: var Lexer): bool =
## Check validity of a primary.
 
if lex.token in {tkIdent, tkInt, tkFalse, tkTrue}:
lex.nextToken()
return true
 
elif lex.token == tkLPar:
lex.nextToken()
if not lex.checkExpr():
return false
if lex.token != tkRPar:
lex.error = "Encountered $#; expected ')'.".format(lex.token)
return false
else:
lex.nextToken()
return true
 
else:
lex.error = "Encountered $#; expected identifier, litteral or '('.".format(lex.token)
return false
 
 
proc checkExpr6(lex: var Lexer): bool =
## Check validity of an expr6.
 
if not lex.checkPrimary(): return false
while lex.token in [tkMul, tkDiv]:
lex.nextToken()
if not lex.checkPrimary(): return false
result = true
 
 
proc checkExpr5(lex: var Lexer): bool =
## Check validity of an expr5.
 
if not lex.checkExpr6(): return false
while lex.token in [tkAdd, tkSub]:
lex.nextToken()
if not lex.checkExpr6(): return false
result = true
 
 
proc checkExpr4(lex: var Lexer): bool =
## Check validity of an expr4.
 
if lex.token == tkNot: lex.nextToken()
if not lex.checkExpr5(): return false
if lex.token in [tkLt, tkEq]:
lex.nextToken()
if not lex.checkExpr5(): return false
result = true
 
 
proc checkExpr3(lex: var Lexer): bool =
## Check validity of an expr3.
 
if not lex.checkExpr4(): return false
while lex.token == tkAnd:
lex.nextToken()
if not lex.checkExpr4(): return false
result = true
 
 
proc checkExpr2(lex: var Lexer): bool =
## Check validity of an expr2.
 
if not lex.checkExpr3(): return false
while lex.token == tkOr:
lex.nextToken()
if not lex.checkExpr3(): return false
result = true
 
 
proc checkExpr(lex: var Lexer): bool =
## Check validity of an expr.
lex.checkExpr2()
 
 
proc checkStmt(lex: var Lexer): bool =
## Check validity of a statement.
 
result = lex.checkExpr()
if result and lex.pos < lex.len:
lex.error = "Extra characters at end of statement."
result = false
 
#———————————————————————————————————————————————————————————————————————————————————————————————————
 
# Using test set from Algol68 version.
 
const Tests = ["wombat",
"wombat or monotreme",
"( wombat and not )",
"wombat or not",
"a + 1",
"a + b < c",
"a + b - c * d / e < f and not ( g = h )",
"a + b - c * d / e < f and not ( g = h",
"a = b",
"a or b = c",
"$",
"true or false = not true",
"not true = false",
"3 + not 5",
"3 + (not 5)",
"(42 + 3",
" not 3 < 4 or (true or 3 / 4 + 8 * 5 - 5 * 2 < 56) and 4 * 3 < 12 or not true",
" and 3 < 2",
"not 7 < 2",
"2 < 3 < 4",
"2 < foobar - 3 < 4",
"2 < foobar and 3 < 4",
"4 * (32 - 16) + 9 = 73",
"235 76 + 1",
"a + b = not c and false",
"a + b = (not c) and false",
"a + b = (not c and false)",
"ab_c / bd2 or < e_f7",
"g not = h",
"été = false",
"i++",
"j & k",
"l or _m"]
 
for test in Tests:
var lex = initLexer(test)
let ok = checkStmt(lex)
echo test, " → ", ok
if not ok: echo "*** Error at position $1. $2 ".format(lex.pos, lex.error)</syntaxhighlight>
 
{{out}}
<pre>wombat → true
wombat or monotreme → true
( wombat and not ) → false
*** Error at position 18. Encountered ')'; expected identifier, litteral or '('.
wombat or not → false
*** Error at position 13. Encountered EOF; expected identifier, litteral or '('.
a + 1 → true
a + b < c → true
a + b - c * d / e < f and not ( g = h ) → true
a + b - c * d / e < f and not ( g = h → false
*** Error at position 37. Encountered EOF; expected ')'.
a = b → true
a or b = c → true
$ → false
*** Error at position 1. Encountered invalid token; expected identifier, litteral or '('.
true or false = not true → false
*** Error at position 19. Encountered 'not'; expected identifier, litteral or '('.
not true = false → true
3 + not 5 → false
*** Error at position 7. Encountered 'not'; expected identifier, litteral or '('.
3 + (not 5) → true
(42 + 3 → false
*** Error at position 7. Encountered EOF; expected ')'.
not 3 < 4 or (true or 3 / 4 + 8 * 5 - 5 * 2 < 56) and 4 * 3 < 12 or not true → true
and 3 < 2 → false
*** Error at position 4. Encountered 'and'; expected identifier, litteral or '('.
not 7 < 2 → true
2 < 3 < 4 → false
*** Error at position 7. Extra characters at end of statement.
2 < foobar - 3 < 4 → false
*** Error at position 16. Extra characters at end of statement.
2 < foobar and 3 < 4 → true
4 * (32 - 16) + 9 = 73 → true
235 76 + 1 → false
*** Error at position 6. Extra characters at end of statement.
a + b = not c and false → false
*** Error at position 11. Encountered 'not'; expected identifier, litteral or '('.
a + b = (not c) and false → true
a + b = (not c and false) → true
ab_c / bd2 or < e_f7 → false
*** Error at position 15. Encountered '<'; expected identifier, litteral or '('.
g not = h → false
*** Error at position 5. Extra characters at end of statement.
été = false → false
*** Error at position 1. Encountered invalid token; expected identifier, litteral or '('.
i++ → false
*** Error at position 3. Encountered '+'; expected identifier, litteral or '('.
j & k → false
*** Error at position 3. Extra characters at end of statement.
l or _m → false
*** Error at position 6. Encountered invalid token; expected identifier, litteral or '('.</pre>
 
=={{header|Perl}}==
Made fix for 'not' see Discussion.
Added 'not' and non-assoc fixes.
<lang>#!/usr/bin/perl
Cooler output.
<syntaxhighlight lang="perl">#!/usr/bin/perl
 
use strict; # http://www.rosettacode.org/wiki/Compiler/_Verifying_Syntax
use strict;
use warnings;
 
sub error { die s/\G/{{\U@_}}/r }
{
sub want { /\G\Q$_[0]/gc ? pop : error $_[1]; 1 }
my $pos = pos($_) // 0;
die $_, ' ' x ($pos + 7), "^ $_[0]\n";
}
sub want { /\G\Q$_[0]/gc or error $_[1] }
 
sub expr
Line 256 ⟶ 1,106:
my $level = shift;
/\G\h+/gc;
/\G\(/gc && want ')', "expected a closing paren", expr(0) or
/\G(true|false)\b/gc or
/\G\(/gc && want ')', "expected a closing paren", expr(0) or
/\G(?=(and|or)\b)/gc && error "expected a primary, '$1' is an operator" or
$level <= 4 && /\Gnot\b/gc && expr(5) or
/\G (?!(?:and|or|not)\b) (?:\d+|[a-zA-Z]\w*)/gcgcx or
error "expected a primary";
/\G\h+/gc ? 1 :
$level <= 2 && /\Gor\b/gc ? expr(23) :
$level <= 3 && /\Gand\b/gc ? expr(4) :
$level <= 4 && /\G[=<]/gc ? expr(4.5) :
$level == 4.5 && /\G(?=[=<])/gc ? error "operator is non-associative operator" :
$level <= 5 && /\G[+-]/gc ? expr(6) :
$level <= 6 && /\G[*\/]/gc ? expr(7) :
return 1 while 1;
}
Line 275 ⟶ 1,123:
{
print eval { want "\n", "expected end of input", expr(0) } ?
" true ${_}true\n" : "$@false\n", "\n $@";
}
 
__DATA__
3 + not 5
3 + (not 5)
(42 + 3
(42 + 3 syntax_error
not 3 < 4 or (true or 3 / 4 + 8 * 5 - 5 * 2 < 56) and 4 * 3 < 12 or not true
not 3 < 4 or (true or 3/4+8*5-5*2 < 56) and 4*3 < 12 or not true
and 3 < 2
not 7 < 2
2 < 3 < 4
2 < foobar - 3 < 4
2 < foobar and 3 < 4
4 * (32 - 16) + 9 = 73
235 76 + 1
a + b = not c and false
a + b = (not c) and false
a + b = (not c and false)
ab_c / bd2 or < e_f7
g not = h
été = false
i++
j & k
l or _m</lang>
UPPER_cAsE_aNd_letter_and_12345_test</syntaxhighlight>
{{out}}
<pre>
false 3 + not 5
(42 + 3{{EXPECTED A CLOSING PAREN}}
^ expected a primary
false
true 3 + (not 5)
false (42 + 3
^ expected a closing paren
false (42 + 3 syntax_error
^ expected a closing paren
true not 3 < 4 or (true or 3/4+8*5-5*2 < 56) and 4*3 < 12 or not true
false and 3 < 2
^ expected a primary
true not 7 < 2
false 2 < 3 < 4
^ non-associative operator
false 2 < foobar - 3 < 4
^ non-associative operator
true 2 < foobar and 3 < 4
true 4 * (32 - 16) + 9 = 73
false 235 76 + 1
^ expected end of input
false a + b = not c and false
^ expected a primary
true a + b = (not c) and false
true a + b = (not c and false)
false ab_c / bd2 or < e_f7
^ expected a primary
false g not = h
^ expected end of input
false i++
^ expected a primary
false j & k
^ expected end of input
false l or _m
^ expected a primary
true UPPER_cAsE_aNd_letter_and_12345_test
</pre>
 
=={{header|Phix}}==
not 3 < 4 or (true or 3 / 4 + 8 * 5 - 5 * 2 < 56) and 4 * 3 < 12 or not true
<!--<syntaxhighlight lang="phix">(phixonline)-->
true
<span style="color: #000080;font-style:italic;">-- demo\rosetta\Compiler\Verify_Syntax.exw</span>
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span>
<span style="color: #004080;">string</span> <span style="color: #000000;">src</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">ch</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">sdx</span>
<span style="color: #008080;">procedure</span> <span style="color: #000000;">skip_spaces</span><span style="color: #0000FF;">()</span>
<span style="color: #008080;">while</span> <span style="color: #000000;">1</span> <span style="color: #008080;">do</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">sdx</span><span style="color: #0000FF;">></span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">src</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span> <span style="color: #008080;">exit</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #000000;">ch</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">src</span><span style="color: #0000FF;">[</span><span style="color: #000000;">sdx</span><span style="color: #0000FF;">]</span>
<span style="color: #008080;">if</span> <span style="color: #008080;">not</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #000000;">ch</span><span style="color: #0000FF;">,{</span><span style="color: #008000;">' '</span><span style="color: #0000FF;">,</span><span style="color: #008000;">'\t'</span><span style="color: #0000FF;">,</span><span style="color: #008000;">'\r'</span><span style="color: #0000FF;">,</span><span style="color: #008000;">'\n'</span><span style="color: #0000FF;">})</span> <span style="color: #008080;">then</span> <span style="color: #008080;">exit</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #000000;">sdx</span> <span style="color: #0000FF;">+=</span> <span style="color: #000000;">1</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">while</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<span style="color: #008080;">enum</span> <span style="color: #000000;">SYMBOL</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">INTEGER</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">IDENT</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">ERROR</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">EOF</span>
<span style="color: #008080;">constant</span> <span style="color: #000000;">toktypes</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #008000;">"SYMBOL"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"INTEGER"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"IDENT"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"ERROR"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"EOF"</span><span style="color: #0000FF;">}</span>
<span style="color: #004080;">sequence</span> <span style="color: #000000;">tok</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">sprintok</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">fmt</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">tok</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">toktypes</span><span style="color: #0000FF;">[</span><span style="color: #000000;">tok</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]]</span>
<span style="color: #008080;">return</span> <span style="color: #7060A8;">sprintf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">fmt</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">tok</span><span style="color: #0000FF;">})</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
<span style="color: #008080;">procedure</span> <span style="color: #000000;">next_token</span><span style="color: #0000FF;">()</span>
<span style="color: #000080;font-style:italic;">-- yeilds one of:
-- {SYMBOL,ch} where ch is one of "()+-/*=&&lt;", or
-- {INTEGER,n}, or
-- {IDENT,string}, or
-- {ERROR,msg}, or
-- {EOF}</span>
<span style="color: #000000;">skip_spaces</span><span style="color: #0000FF;">()</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">tokstart</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">sdx</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">tok</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]=</span><span style="color: #000000;">ERROR</span> <span style="color: #008080;">then</span>
<span style="color: #0000FF;">?{</span><span style="color: #008000;">"erm, tok is"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">tok</span><span style="color: #0000FF;">}</span> <span style="color: #000080;font-style:italic;">-- looping??</span>
<span style="color: #008080;">elsif</span> <span style="color: #000000;">sdx</span><span style="color: #0000FF;">></span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">src</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">tok</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">EOF</span><span style="color: #0000FF;">}</span>
<span style="color: #008080;">elsif</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #000000;">ch</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"()+-/*=&&lt;"</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">sdx</span> <span style="color: #0000FF;">+=</span> <span style="color: #000000;">1</span>
<span style="color: #000000;">tok</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">SYMBOL</span><span style="color: #0000FF;">,</span><span style="color: #000000;">ch</span><span style="color: #0000FF;">&</span><span style="color: #008000;">""</span><span style="color: #0000FF;">}</span>
<span style="color: #008080;">elsif</span> <span style="color: #0000FF;">(</span><span style="color: #000000;">ch</span><span style="color: #0000FF;">>=</span><span style="color: #008000;">'0'</span> <span style="color: #008080;">and</span> <span style="color: #000000;">ch</span><span style="color: #0000FF;"><=</span><span style="color: #008000;">'9'</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">n</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">ch</span><span style="color: #0000FF;">-</span><span style="color: #008000;">'0'</span>
<span style="color: #008080;">while</span> <span style="color: #004600;">true</span> <span style="color: #008080;">do</span>
<span style="color: #000000;">sdx</span> <span style="color: #0000FF;">+=</span> <span style="color: #000000;">1</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">sdx</span><span style="color: #0000FF;">></span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">src</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span> <span style="color: #008080;">exit</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #000000;">ch</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">src</span><span style="color: #0000FF;">[</span><span style="color: #000000;">sdx</span><span style="color: #0000FF;">]</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">ch</span><span style="color: #0000FF;"><</span><span style="color: #008000;">'0'</span> <span style="color: #008080;">or</span> <span style="color: #000000;">ch</span><span style="color: #0000FF;">></span><span style="color: #008000;">'9'</span> <span style="color: #008080;">then</span> <span style="color: #008080;">exit</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #000000;">n</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">n</span><span style="color: #0000FF;">*</span><span style="color: #000000;">10</span> <span style="color: #0000FF;">+</span> <span style="color: #000000;">ch</span><span style="color: #0000FF;">-</span><span style="color: #008000;">'0'</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">while</span>
<span style="color: #000000;">tok</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">INTEGER</span><span style="color: #0000FF;">,</span><span style="color: #000000;">n</span><span style="color: #0000FF;">}</span>
<span style="color: #008080;">elsif</span> <span style="color: #0000FF;">(</span><span style="color: #000000;">ch</span><span style="color: #0000FF;">>=</span><span style="color: #008000;">'a'</span> <span style="color: #008080;">and</span> <span style="color: #000000;">ch</span><span style="color: #0000FF;"><=</span><span style="color: #008000;">'z'</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">or</span> <span style="color: #0000FF;">(</span><span style="color: #000000;">ch</span><span style="color: #0000FF;">>=</span><span style="color: #008000;">'A'</span> <span style="color: #008080;">and</span> <span style="color: #000000;">ch</span><span style="color: #0000FF;"><=</span><span style="color: #008000;">'Z'</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span>
<span style="color: #008080;">while</span> <span style="color: #004600;">true</span> <span style="color: #008080;">do</span>
<span style="color: #000000;">sdx</span> <span style="color: #0000FF;">+=</span> <span style="color: #000000;">1</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">sdx</span><span style="color: #0000FF;">></span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">src</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span> <span style="color: #008080;">exit</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #000000;">ch</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">src</span><span style="color: #0000FF;">[</span><span style="color: #000000;">sdx</span><span style="color: #0000FF;">]</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">ch</span><span style="color: #0000FF;">!=</span><span style="color: #008000;">'_'</span>
<span style="color: #008080;">and</span> <span style="color: #0000FF;">(</span><span style="color: #000000;">ch</span><span style="color: #0000FF;"><</span><span style="color: #008000;">'a'</span> <span style="color: #008080;">or</span> <span style="color: #000000;">ch</span><span style="color: #0000FF;">></span><span style="color: #008000;">'z'</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">and</span> <span style="color: #0000FF;">(</span><span style="color: #000000;">ch</span><span style="color: #0000FF;"><</span><span style="color: #008000;">'A'</span> <span style="color: #008080;">or</span> <span style="color: #000000;">ch</span><span style="color: #0000FF;">></span><span style="color: #008000;">'Z'</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">and</span> <span style="color: #0000FF;">(</span><span style="color: #000000;">ch</span><span style="color: #0000FF;"><</span><span style="color: #008000;">'0'</span> <span style="color: #008080;">or</span> <span style="color: #000000;">ch</span><span style="color: #0000FF;">></span><span style="color: #008000;">'9'</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span>
<span style="color: #008080;">exit</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">while</span>
<span style="color: #000000;">tok</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">IDENT</span><span style="color: #0000FF;">,</span><span style="color: #000000;">src</span><span style="color: #0000FF;">[</span><span style="color: #000000;">tokstart</span><span style="color: #0000FF;">..</span><span style="color: #000000;">sdx</span><span style="color: #0000FF;">-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]}</span>
<span style="color: #008080;">elsif</span> <span style="color: #000000;">ch</span><span style="color: #0000FF;">=</span><span style="color: #008000;">'_'</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">tok</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">ERROR</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"identifiers may not start with _"</span><span style="color: #0000FF;">}</span>
<span style="color: #000000;">sdx</span> <span style="color: #0000FF;">+=</span> <span style="color: #000000;">1</span>
<span style="color: #008080;">else</span>
<span style="color: #000000;">tok</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">ERROR</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">sprintf</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"illegal char (%c/%d)"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">ch</span><span style="color: #0000FF;">)}</span>
<span style="color: #000000;">sdx</span> <span style="color: #0000FF;">+=</span> <span style="color: #000000;">1</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<span style="color: #008080;">forward</span> <span style="color: #008080;">procedure</span> <span style="color: #000000;">or_expr</span><span style="color: #0000FF;">()</span>
<span style="color: #008080;">procedure</span> <span style="color: #000000;">primary</span><span style="color: #0000FF;">()</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">tt</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">tok</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">tt</span><span style="color: #0000FF;">=</span><span style="color: #000000;">IDENT</span>
<span style="color: #008080;">or</span> <span style="color: #000000;">tt</span><span style="color: #0000FF;">=</span><span style="color: #000000;">INTEGER</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">next_token</span><span style="color: #0000FF;">()</span>
<span style="color: #008080;">elsif</span> <span style="color: #000000;">tok</span><span style="color: #0000FF;">={</span><span style="color: #000000;">SYMBOL</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"("</span><span style="color: #0000FF;">}</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">next_token</span><span style="color: #0000FF;">()</span>
<span style="color: #000000;">or_expr</span><span style="color: #0000FF;">()</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">tok</span><span style="color: #0000FF;">!={</span><span style="color: #000000;">SYMBOL</span><span style="color: #0000FF;">,</span><span style="color: #008000;">")"</span><span style="color: #0000FF;">}</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">tok</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">ERROR</span><span style="color: #0000FF;">,</span><span style="color: #008000;">") expected"</span><span style="color: #0000FF;">}</span>
<span style="color: #008080;">else</span>
<span style="color: #000000;">next_token</span><span style="color: #0000FF;">()</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">else</span>
<span style="color: #000000;">tok</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">ERROR</span><span style="color: #0000FF;">,</span><span style="color: #000000;">sprintok</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"invalid [%v]"</span><span style="color: #0000FF;">)}</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<span style="color: #008080;">procedure</span> <span style="color: #000000;">mul_expr</span><span style="color: #0000FF;">()</span>
<span style="color: #008080;">while</span> <span style="color: #004600;">true</span> <span style="color: #008080;">do</span>
<span style="color: #000000;">primary</span><span style="color: #0000FF;">()</span>
<span style="color: #008080;">if</span> <span style="color: #008080;">not</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #000000;">tok</span><span style="color: #0000FF;">,{{</span><span style="color: #000000;">SYMBOL</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"*"</span><span style="color: #0000FF;">},{</span><span style="color: #000000;">SYMBOL</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"/"</span><span style="color: #0000FF;">}})</span> <span style="color: #008080;">then</span> <span style="color: #008080;">exit</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #000000;">next_token</span><span style="color: #0000FF;">()</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">while</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<span style="color: #008080;">procedure</span> <span style="color: #000000;">sum_expr</span><span style="color: #0000FF;">()</span>
<span style="color: #008080;">while</span> <span style="color: #004600;">true</span> <span style="color: #008080;">do</span>
<span style="color: #000000;">mul_expr</span><span style="color: #0000FF;">()</span>
<span style="color: #008080;">if</span> <span style="color: #008080;">not</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #000000;">tok</span><span style="color: #0000FF;">,{{</span><span style="color: #000000;">SYMBOL</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"+"</span><span style="color: #0000FF;">},{</span><span style="color: #000000;">SYMBOL</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"-"</span><span style="color: #0000FF;">}})</span> <span style="color: #008080;">then</span> <span style="color: #008080;">exit</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #000000;">next_token</span><span style="color: #0000FF;">()</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">while</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<span style="color: #008080;">procedure</span> <span style="color: #000000;">cmp_expr</span><span style="color: #0000FF;">()</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">tok</span><span style="color: #0000FF;">=={</span><span style="color: #000000;">IDENT</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"not"</span><span style="color: #0000FF;">}</span> <span style="color: #008080;">then</span> <span style="color: #000000;">next_token</span><span style="color: #0000FF;">()</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #000080;font-style:italic;">-- while true do
-- sum_expr()
-- if not find(tok,{{SYMBOL,"="},{SYMBOL,"&lt;"}}) then exit end if
-- next_token()
-- end while</span>
<span style="color: #000000;">sum_expr</span><span style="color: #0000FF;">()</span>
<span style="color: #008080;">if</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #000000;">tok</span><span style="color: #0000FF;">,{{</span><span style="color: #000000;">SYMBOL</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"="</span><span style="color: #0000FF;">},{</span><span style="color: #000000;">SYMBOL</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"&lt;"</span><span style="color: #0000FF;">}})</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">next_token</span><span style="color: #0000FF;">()</span>
<span style="color: #000000;">sum_expr</span><span style="color: #0000FF;">()</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<span style="color: #008080;">procedure</span> <span style="color: #000000;">and_expr</span><span style="color: #0000FF;">()</span>
<span style="color: #008080;">while</span> <span style="color: #004600;">true</span> <span style="color: #008080;">do</span>
<span style="color: #000000;">cmp_expr</span><span style="color: #0000FF;">()</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">tok</span><span style="color: #0000FF;">!={</span><span style="color: #000000;">IDENT</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"and"</span><span style="color: #0000FF;">}</span> <span style="color: #008080;">then</span> <span style="color: #008080;">exit</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #000000;">next_token</span><span style="color: #0000FF;">()</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">while</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<span style="color: #008080;">procedure</span> <span style="color: #000000;">or_expr</span><span style="color: #0000FF;">()</span>
<span style="color: #008080;">while</span> <span style="color: #004600;">true</span> <span style="color: #008080;">do</span>
<span style="color: #000000;">and_expr</span><span style="color: #0000FF;">()</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">tok</span><span style="color: #0000FF;">!={</span><span style="color: #000000;">IDENT</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"or"</span><span style="color: #0000FF;">}</span> <span style="color: #008080;">then</span> <span style="color: #008080;">exit</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #000000;">next_token</span><span style="color: #0000FF;">()</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">while</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<span style="color: #008080;">procedure</span> <span style="color: #000000;">statement</span><span style="color: #0000FF;">()</span>
<span style="color: #000000;">or_expr</span><span style="color: #0000FF;">()</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<span style="color: #008080;">procedure</span> <span style="color: #000000;">verify_syntax</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">source</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">src</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">source</span>
<span style="color: #000000;">sdx</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">1</span>
<span style="color: #000000;">tok</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">0</span><span style="color: #0000FF;">}</span> <span style="color: #000080;font-style:italic;">-- ("not error"/invalid-ish)</span>
<span style="color: #000000;">next_token</span><span style="color: #0000FF;">()</span>
<span style="color: #000000;">statement</span><span style="color: #0000FF;">()</span>
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"%30s ==&gt; %s\n"</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">source</span><span style="color: #0000FF;">,</span><span style="color: #008080;">iff</span><span style="color: #0000FF;">(</span><span style="color: #000000;">tok</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]=</span><span style="color: #000000;">EOF</span><span style="color: #0000FF;">?</span><span style="color: #008000;">"true"</span><span style="color: #0000FF;">:</span><span style="color: #000000;">sprintok</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"false [tok=%v]"</span><span style="color: #0000FF;">))})</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<span style="color: #008080;">constant</span> <span style="color: #000000;">tests</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span>
<span style="color: #008000;">"$"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"one"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"either or both"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"a + 1"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"a + b &lt; c"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"a = b"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"a or b = c"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"3 + not 5"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"3 + (not 5)"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"(42 + 3"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"(42 + 3)"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">" not 3 &lt; 4 or (true or 3 / 4 + 8 * 5 - 5 * 2 &lt; 56) and 4 * 3 &lt; 12 or not true"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">" and 3 &lt; 2"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"not 7 &lt; 2"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"2 &lt; 3 &lt; 4"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"2 &lt; (3 &lt; 4)"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"2 &lt; foobar - 3 &lt; 4"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"2 &lt; foobar and 3 &lt; 4"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"4 * (32 - 16) + 9 = 73"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"235 76 + 1"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"true or false = not true"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"true or false = (not true)"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"not true or false = false"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"not true = false"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"a + b = not c and false"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"a + b = (not c) and false"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"a + b = (not c and false)"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"ab_c / bd2 or &lt; e_f7"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"g not = h"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"i++"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"j & k"</span><span style="color: #0000FF;">,</span>
<span style="color: #008000;">"l or _m"</span><span style="color: #0000FF;">}</span>
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"Verify Syntax:\n"</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">tests</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
<span style="color: #000000;">verify_syntax</span><span style="color: #0000FF;">(</span><span style="color: #000000;">tests</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">])</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #0000FF;">?</span><span style="color: #008000;">"done"</span>
<span style="color: #0000FF;">{}</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">wait_key</span><span style="color: #0000FF;">()</span>
<!--</syntaxhighlight>-->
{{out}}
Note that "= not c" fails, whereas "= (not c)" passes, see talk page. (Arguably the task definition should be fixed.)
<pre>
Verify Syntax:
$ ==> false [tok={"ERROR",`invalid [{"ERROR","illegal char ($/36)"}]`}]
one ==> true
either or both ==> true
a + 1 ==> true
a + b < c ==> true
a = b ==> true
a or b = c ==> true
3 + not 5 ==> false [tok={"INTEGER",5}]
3 + (not 5) ==> true
(42 + 3 ==> false [tok={"ERROR",") expected"}]
(42 + 3) ==> true
not 3 < 4 or (true or 3 / 4 + 8 * 5 - 5 * 2 < 56) and 4 * 3 < 12 or not true ==> true
and 3 < 2 ==> false [tok={"INTEGER",3}]
not 7 < 2 ==> true
2 < 3 < 4 ==> false [tok={"SYMBOL","<"}]
2 < (3 < 4) ==> true
2 < foobar - 3 < 4 ==> false [tok={"SYMBOL","<"}]
2 < foobar and 3 < 4 ==> true
4 * (32 - 16) + 9 = 73 ==> true
235 76 + 1 ==> false [tok={"INTEGER",76}]
true or false = not true ==> false [tok={"IDENT","true"}]
true or false = (not true) ==> true
not true or false = false ==> true
not true = false ==> true
a + b = not c and false ==> false [tok={"IDENT","c"}]
a + b = (not c) and false ==> true
a + b = (not c and false) ==> true
ab_c / bd2 or < e_f7 ==> false [tok={"ERROR",`invalid [{"SYMBOL","<"}]`}]
g not = h ==> false [tok={"IDENT","not"}]
i++ ==> false [tok={"ERROR",`invalid [{"SYMBOL","+"}]`}]
j & k ==> false [tok={"SYMBOL","&"}]
l or _m ==> false [tok={"ERROR",`invalid [{"ERROR","identifiers may not start with _"}]`}]
</pre>
 
=={{header|Raku}}==
{{EXPECTED A PRIMARY, 'AND' IS AN OPERATOR}}and 3 < 2
Format of task grammar is changed from EBNF to ABNF to cater for the Grammar::ABNF module and testing data is taken from the Perl entry.
false
<syntaxhighlight lang="raku" line># 20200511 Raku programming solution
 
use Grammar::ABNF;
not 7 < 2
true
 
grammar G is Grammar::ABNF { token CRLF { "\n" } };
2 < 3 {{OPERATOR IS NON-ASSOCIATIVE}}< 4
false
 
my $g = G.generate(Q:to<RULES>
stmt = expr
expr = expr_level_2
expr_level_2 = expr_level_3 *( " or " expr_level_3 )
expr_level_3 = expr_level_4 *( " and " expr_level_4 )
expr_level_4 = ["not "] expr_level_5 [ [ " = " / " < " ] expr_level_5 ]
expr_level_5 = expr_level_6 *( [ " + " / " - " ] expr_level_6 )
expr_level_6 = primary *( [ " * " / " / " ] primary )
Integer = Digit *( Digit )
Identifier = Letter *( Letter / Digit / "_" )
primary = Identifier / Integer / "(" expr ")" / " true " / " false "
Digit = "0" / "1" / "2" / "3" / "4" / "5" / "6" / "7" / "8" / "9"
Letter = "a" / "b" / "c" / "d" / "e" / "f" / "g" / "h" / "i" / "j"
/ "k" / "l" / "m" / "n" / "o" / "p" / "q" / "r" / "s" / "t"
/ "u" / "v" / "w" / "x" / "y" / "z" / "A" / "B" / "C" / "D"
/ "E" / "F" / "G" / "H" / "I" / "J" / "K" / "L" / "M" / "N"
/ "O" / "P" / "Q" / "R" / "S" / "T" / "U" / "V" / "W" / "X"
/ "Y" / "Z"
RULES
);
 
my \DATA = Q:to<DATA>;
3 + not 5
3 + (not 5)
(42 + 3
(42 + 3 syntax_error
not 3 < 4 or (true or 3 / 4 + 8 * 5 - 5 * 2 < 56) and 4 * 3 < 12 or not true
and 3 < 2
not 7 < 2
2 < 3 < 4
2 < foobar - 3 < 4
2 < foobar and 3 < 4
4 * (32 - 16) + 9 = 73
235 76 + 1
true
a + b = not c and false
a + b = (not c) and false
a + b = (not c and false)
ab_c / bd2 or < e_f7
g not = h
i++
j & k
l or _m
UPPER_cAsE_aNd_letter_and_12345_test
DATA
 
say $g.parse($_).Bool, "\t", $_ for DATA.lines</syntaxhighlight>
235 {{EXPECTED END OF INPUT}}76 + 1
{{out}}
false
<pre>False 3 + not 5
True 3 + (not 5)
False (42 + 3
False (42 + 3 syntax_error
True not 3 < 4 or (true or 3 / 4 + 8 * 5 - 5 * 2 < 56) and 4 * 3 < 12 or not true
False and 3 < 2
True not 7 < 2
False 2 < 3 < 4
False 2 < foobar - 3 < 4
True 2 < foobar and 3 < 4
True 4 * (32 - 16) + 9 = 73
False 235 76 + 1
False a + b = not c and false
True a + b = (not c) and false
True a + b = (not c and false)
False ab_c / bd2 or < e_f7
False g not = h
False i++
False j & k
False l or _m
True UPPER_cAsE_aNd_letter_and_12345_test
</pre>
 
=={{header|Wren}}==
a + b = not {{EXPECTED END OF INPUT}}c and false
{{trans|Nim}}
false
{{libheader|Wren-dynamic}}
{{libheader|Wren-str}}
<syntaxhighlight lang="wren">import "./dynamic" for Enum
import "./str" for Char
 
var Token = Enum.create(
a + b = (not c) and false
"Token", ["error", "ident", "int", "lpar", "rpar", "False", "True",
true
"lt", "eq", "add", "sub", "mul", "div", "or", "and", "not", "eof"]
)
 
var Token2Text = {
ab_c / bd2 or {{EXPECTED A PRIMARY}}< e_f7
Token.error: "invalid token", Token.ident: "identifier", Token.int: "integer",
false
Token.lpar: "'('", Token.rpar: "')'", Token.False: "'false'", Token.True: "'true'",
Token.lt: "'<'", Token.eq: "'='", Token.add: "'+'", Token.sub: "'-'",
Token.mul: "'*'", Token.div: "'/'", Token.or: "'or'", Token.and: "'and'",
Token.not: "'not'", Token.eof: "EOF"
}
 
var IdentTokens = {
g {{EXPECTED END OF INPUT}}not = h
"false": Token.False, "true": Token.True, "or": Token.or,
false
"and": Token.and, "not": Token.not
}
 
var CharTokens = {
{{EXPECTED A PRIMARY}}été = false
"(": Token.lpar, ")": Token.rpar, "<": Token.lt, "=": Token.eq,
false
"+": Token.add, "-": Token.sub, "*": Token.mul, "/": Token.div
}
 
var IsIdentChar = Fn.new { |c| Char.isAsciiAlphaNum(c) || c == "_" }
i+{{EXPECTED A PRIMARY}}+
false
 
class Lexer {
j {{EXPECTED END OF INPUT}}& k
static init(s) {
false
var lex = Lexer.new(s, s.count, 0, "", "")
lex.nextToken
return lex
}
 
construct new(str, len, pos, token, error) {
l or {{EXPECTED A PRIMARY}}_m
_str = str // string to parse
false
_len = len // string length
</pre>
_pos = pos // current lexer position
_token = token // current token
_error = error // error message
}
 
// property getters required
=={{header|Phix}}==
pos { _pos }
<lang Phix>-- demo\rosetta\Compiler\Verify_Syntax.exw
error { _error }
string src
integer ch, sdx
procedure skip_spaces()
while 1 do
if sdx>length(src) then exit end if
ch = src[sdx]
if not find(ch," \t\r\n") then exit end if
sdx += 1
end while
end procedure
enum SYMBOL, STRING, INTEGER, IDENT, ERROR, EOF
constant toktypes = {"SYMBOL","STRING","INTEGER","IDENT","ERROR","EOF"}
sequence tok
 
// get the token for an identifier
function sprintok(string fmt)
getIdToken {
tok[1] = toktypes[tok[1]]
var s = ""
return sprintf(fmt,{tok})
while (_pos < _len && IsIdentChar.call(_str[_pos])) {
end function
s = s + _str[_pos]
_pos = _pos + 1
}
_token = IdentTokens.containsKey(s) ? IdentTokens[s] : Token.ident
}
 
// get an integer token
procedure next_token()
getInt {
-- yeilds a single character token, one of:
while (_pos < _len && Char.isDigit(_str[_pos])) _pos = _pos + 1
-- {SYMBOL,ch} where ch is one of "{}()[]|=.;", or
_token = Token.int
-- {STRING,string}, or
}
-- {INTEGER,n}, or
-- {IDENT,string}, or
-- {ERROR,msg}, or
-- {EOF}
skip_spaces()
integer tokstart = sdx
if tok[1]=ERROR then
?{"erm, tok is",tok} -- looping??
elsif sdx>length(src) then
tok = {EOF}
elsif find(ch,"()+-/*=&<") then
sdx += 1
tok = {SYMBOL,ch&""}
elsif ch='\"'
or ch='\'' then
integer closech = ch
tok = {ERROR,"no closing quote"}
sdx += 1
for tokend=sdx to length(src) do
if src[tokend]=closech then
sdx = tokend+1
tok = {STRING,src[tokstart+1..tokend-1]}
exit
end if
end for
elsif (ch>='0' and ch<='9') then
integer n = ch-'0'
while true do
sdx += 1
if sdx>length(src) then exit end if
ch = src[sdx]
if ch<'0' or ch>'9' then exit end if
n = n*10 + ch-'0'
end while
tok = {INTEGER,n}
elsif (ch>='a' and ch<='z')
or (ch>='A' and ch<='Z') then
while true do
sdx += 1
if sdx>length(src) then exit end if
ch = src[sdx]
if ch!='_'
and (ch<'a' or ch>'z')
and (ch<'A' or ch>'Z')
and (ch<'0' or ch>'9') then
exit
end if
end while
tok = {IDENT,src[tokstart..sdx-1]}
elsif ch='_' then
tok = {ERROR,"identifiers may not start with _"}
sdx += 1
else
tok = {ERROR,sprintf("illegal char (%c/%d)",ch)}
sdx += 1
end if
end procedure
 
// find the next token
forward procedure or_expr()
nextToken {
// skip spaces
while (_pos < _len && _str[_pos] == " ") _pos = _pos + 1
if (_pos == _len) {
_token = Token.eof
} else {
var ch = _str[_pos]
if (Char.isAsciiLower(ch)) {
getIdToken
} else if (Char.isDigit(ch)) {
getInt
} else {
_pos = _pos + 1
_token = CharTokens.containsKey(ch) ? CharTokens[ch] : Token.error
}
}
}
 
procedure // check validity of a primary()
checkPrimary {
integer tt = tok[1]
if ([Token.ident, Token.int, Token.False, Token.True].contains(_token)) {
if tt=IDENT
or tt=INTEGER nextToken
return true
or (tt=STRING and find(tok[2],{"true","false"})) then
next_token} else if (_token == Token.lpar) {
nextToken
elsif tok={SYMBOL,"("} then
next_token if (!checkExpr) return false
or_expr if (_token != Token.rpar) {
_error = "Encountered %(Token2Text[_token]); expected ')'"
if tok!={SYMBOL,")"} then
tok = {ERROR,") expected"} return false
} else {
next_token() nextToken
end if return true
else }
tok} =else {ERROR,sprintok("invalid [%v]")}
_error = "Encountered %(Token2Text[_token]); expected identifier, literal or '('"
end if
return false
end procedure
}
}
 
// check validity of an expr6
procedure mul_expr()
whilecheckExpr6 true do{
primaryif (!checkPrimary) return false
while ([Token.mul, Token.div].contains(_token)) {
if not find(tok,{{SYMBOL,"*"},{SYMBOL,"/"}}) then exit end if
next_token() nextToken
if (!checkPrimary) return false
end while
}
end procedure
return true
}
 
// check validity of an expr5
procedure sum_expr()
whilecheckExpr5 true do{
mul_exprif (!checkExpr6) return false
while ([Token.add, Token.sub].contains(_token)) {
if not find(tok,{{SYMBOL,"+"},{SYMBOL,"-"}}) then exit end if
next_token() nextToken
if (!checkExpr6) return false
end while
}
end procedure
return true
}
 
// check validity of an expr4
procedure cmp_expr()
checkExpr4 {
if tok=={IDENT,"not"} then next_token() end if
if (_token == Token.not) nextToken
while true do
sum_exprif (!checkExpr5) return false
if ([Token.lt, Token.eq].contains(_token)) {
if not find(tok,{{SYMBOL,"="},{SYMBOL,"<"}}) then exit end if
next_token() nextToken
if (!checkExpr5) return false
end while
}
end procedure
return true
}
 
// check validity of an expr3
procedure and_expr()
whilecheckExpr3 true do{
cmp_exprif (!checkExpr4) return false
ifwhile (_token tok!={IDENT,"= Token.and"}) then exit end if{
next_token() nextToken
if (!checkExpr4) return false
end while
}
end procedure
return true
}
 
// check validity of an expr2
procedure or_expr()
whilecheckExpr2 true do{
and_exprif (!checkExpr3) return false
ifwhile (_token tok!={IDENT,"= Token.or"}) then exit end if{
next_token() nextToken
if (!checkExpr3) return false
end while
}
end procedure
return true
}
 
// check validity of an expr
procedure statement()
checkExpr { checkExpr2 }
or_expr()
end procedure
procedure verify_syntax(string source)
src = source
sdx = 1
tok = {0} -- ("not error"/invalid-ish)
next_token()
statement()
printf(1,"%30s ==> %s\n",{source,iff(tok[1]=EOF?"pass":sprintok("fail [tok=%v]"))})
end procedure
 
// check validity of a statement
constant tests = {
checkStmt {
"(42 + 3",
var result = checkExpr
" not 3 < 4 or (true or 3 / 4 + 8 * 5 - 5 * 2 < 56) and 4 * 3 < 12 or not true",
"if and(result 3&& _pos < 2",_len) {
_error = "notExtra characters at 7end <of 2statement.",
"4 * (32 - 16) + 9result = 73",false
"235 76 + 1",}
"areturn + b = not c and false",result
}
"a + b = (not c) and false",
}
"ab_c / bd2 or < e_f7",
 
"g not = h",
// using test set from Algol68 version
"été = false",
 
"i++",
var tests = [
"j & k",
"l or _mwombat"},
"wombat or monotreme",
"( wombat and not )",
"wombat or not",
"a + 1",
"a + b < c",
"a + b - c * d / e < f and not ( g = h )",
"a + b - c * d / e < f and not ( g = h",
"a = b",
"a or b = c",
"$",
"true or false = not true",
"not true = false",
"3 + not 5",
"3 + (not 5)",
"(42 + 3",
" not 3 < 4 or (true or 3 / 4 + 8 * 5 - 5 * 2 < 56) and 4 * 3 < 12 or not true",
" and 3 < 2",
"not 7 < 2",
"2 < 3 < 4",
"2 < foobar - 3 < 4",
"2 < foobar and 3 < 4",
"4 * (32 - 16) + 9 = 73",
"235 76 + 1",
"a + b = not c and false",
"a + b = (not c) and false",
"a + b = (not c and false)",
"ab_c / bd2 or < e_f7",
"g not = h",
"été = false",
"i++",
"j & k",
"l or _m"
]
 
for (test in tests) {
var lex = Lexer.init(test)
var ok = lex.checkStmt
System.print("%(test) -> %(ok)")
if (!ok) {
System.print("*** Error at position %(lex.pos). %(lex.error)\n")
}
}</syntaxhighlight>
 
for i=1 to length(tests) do
verify_syntax(tests[i])
end for</lang>
{{out}}
Note that "= not c" fails, whereas "= (not c)" passes, see talk page.
<pre>
wombat -> true
(42 + 3 ==> fail [tok={"ERROR",") expected"}]
wombat or monotreme -> true
not 3 < 4 or (true or 3 / 4 + 8 * 5 - 5 * 2 < 56) and 4 * 3 < 12 or not true ==> pass
( wombat and not ) -> false
and 3 < 2 ==> fail [tok={"INTEGER",3}]
*** Error at position 18. Encountered ')'; expected identifier, literal or '('
not 7 < 2 ==> pass
 
4 * (32 - 16) + 9 = 73 ==> pass
wombat or not -> false
235 76 + 1 ==> fail [tok={"INTEGER",76}]
*** Error at position 13. Encountered EOF; expected identifier, literal or '('
a + b = not c and false ==> fail [tok={"IDENT","c"}]
 
a + b = (not c) and false ==> pass
a + 1 -> true
ab_c / bd2 or < e_f7 ==> fail [tok={"ERROR",`invalid [{"SYMBOL","<"}]`}]
a + b < c -> true
g not = h ==> fail [tok={"IDENT","not"}]
a + b - c * d / e < f and not ( g = h ) -> true
â??®tâ??® = false ==> fail [tok={"ERROR",`invalid [{"ERROR","illegal char (â??/195)"}]`}]
a + b - c * d / e < f and not ( g = h -> false
i++ ==> fail [tok={"ERROR",`invalid [{"SYMBOL","+"}]`}]
*** Error at position 37. Encountered EOF; expected ')'
j & k ==> fail [tok={"SYMBOL","&"}]
 
l or _m ==> fail [tok={"ERROR",`invalid [{"ERROR","identifiers may not start with _"}]`}]
a = b -> true
a or b = c -> true
$ -> false
*** Error at position 1. Encountered invalid token; expected identifier, literal or '('
 
true or false = not true -> false
*** Error at position 19. Encountered 'not'; expected identifier, literal or '('
 
not true = false -> true
3 + not 5 -> false
*** Error at position 7. Encountered 'not'; expected identifier, literal or '('
 
3 + (not 5) -> true
(42 + 3 -> false
*** Error at position 7. Encountered EOF; expected ')'
 
not 3 < 4 or (true or 3 / 4 + 8 * 5 - 5 * 2 < 56) and 4 * 3 < 12 or not true -> true
and 3 < 2 -> false
*** Error at position 4. Encountered 'and'; expected identifier, literal or '('
 
not 7 < 2 -> true
2 < 3 < 4 -> false
*** Error at position 7. Extra characters at end of statement.
 
2 < foobar - 3 < 4 -> false
*** Error at position 16. Extra characters at end of statement.
 
2 < foobar and 3 < 4 -> true
4 * (32 - 16) + 9 = 73 -> true
235 76 + 1 -> false
*** Error at position 6. Extra characters at end of statement.
 
a + b = not c and false -> false
*** Error at position 11. Encountered 'not'; expected identifier, literal or '('
 
a + b = (not c) and false -> true
a + b = (not c and false) -> true
ab_c / bd2 or < e_f7 -> false
*** Error at position 15. Encountered '<'; expected identifier, literal or '('
 
g not = h -> false
*** Error at position 5. Extra characters at end of statement.
 
été = false -> false
*** Error at position 1. Encountered invalid token; expected identifier, literal or '('
 
i++ -> false
*** Error at position 3. Encountered '+'; expected identifier, literal or '('
 
j & k -> false
*** Error at position 3. Extra characters at end of statement.
 
l or _m -> false
*** Error at position 6. Encountered invalid token; expected identifier, literal or '('
</pre>
9,483

edits