Parse EBNF: Difference between revisions
Content added Content deleted
(Want to {{improve}} both examples.) |
(→{{header|Tcl}}: Add in-progress, incomplete Ruby.) |
||
Line 52: | Line 52: | ||
: (parse "(1+2+3)*-4/(1+2)") |
: (parse "(1+2+3)*-4/(1+2)") |
||
-> "DIV {MULT {PLUS {PLUS 1 2} 3} -4} {PLUS 1 2}"</pre> |
-> "DIV {MULT {PLUS {PLUS 1 2} 3} -4} {PLUS 1 2}"</pre> |
||
=={{header|Ruby}}== |
|||
{{in progress|lang=Ruby|day=12|month=May|year=2011}} |
|||
{{incomplete|Ruby|This code divides the input into lexical tokens, but does not parse the tokens by grammar.}} |
|||
<lang ruby>require 'strscan' |
|||
Line = Struct.new :where, :str |
|||
Token = Struct.new :cat, :str, :line, :pos |
|||
# Reads and returns the next Token. At end of file, returns nil. |
|||
def next_token |
|||
# Loop until we reach a Token. |
|||
loop do |
|||
# If before first line, or if at end of line, |
|||
# then get next line, or else declare end of file. |
|||
if (not @scanner) or @scanner.eos? |
|||
if s = @in.gets |
|||
# Each line needs a new Line object. Tokens can hold references |
|||
# to old Line objects. |
|||
@line = Line.new("#{@filename}:#{@in.lineno}", s) |
|||
@scanner = StringScanner.new(s) |
|||
else |
|||
return nil # End of file |
|||
end |
|||
end |
|||
# Skip whitespace. |
|||
break unless @scanner.skip(/[[:space:]]+/) |
|||
end |
|||
# Find token by regexp. |
|||
# The :unknown regexp must not swallow any good tokens. |
|||
if s = @scanner.scan(/:/) |
|||
c = :colon |
|||
elsif s = @scanner.scan(/;/) |
|||
c = :semicolon |
|||
elsif s = @scanner.scan(/\(/) |
|||
c = :group |
|||
elsif s = @scanner.scan(/\)\?/) |
|||
c = :option |
|||
elsif s = @scanner.scan(/\)\*/) |
|||
c = :repeat |
|||
elsif s = @scanner.scan(/\)/) |
|||
c = :one |
|||
elsif s = @scanner.scan(/\|/) |
|||
c = :bar |
|||
elsif s = @scanner.scan(/'[^']*'|"[^"]*"/) |
|||
c = :string |
|||
elsif s = @scanner.scan(/[[:alpha:]][[:alnum:]]*/) |
|||
c = :ident |
|||
elsif s = @scanner.scan(/[^:;()'"[:alpha:][:space:]]*/) |
|||
c = :unknown |
|||
end |
|||
Token.new(c, s, @line, (@scanner.pos - s.length)) |
|||
end |
|||
def read_it |
|||
# TODO: this only dumps the tokens. |
|||
while t = next_token |
|||
p t |
|||
end |
|||
end |
|||
# Read files from ARGV, or else read STDIN. |
|||
case ARGV.length |
|||
when 0 then @filename = "-" |
|||
when 1 then @filename = ARGV[0] |
|||
else fail "Too many arguments" |
|||
end |
|||
open(@filename) { |f| @in = f; read_it }</lang> |
|||
=={{header|Tcl}}== |
=={{header|Tcl}}== |