Thanks, Robert. Here is a fixed version of support.h with improved error
handling.

Long

On Sat, Nov 24, 2012 at 11:37 PM, Robert Feldt <[email protected]>wrote:

> Thanks Long, this was a great meta-II to build on.
>
> I had to fix a bug in support.h (see diff at bottom) but then used your
> code to bootstrap the following, self-contained (i.e. contains its own
> support code (a bit hackish but good for learning)) Ruby version in 28
> non-empty lines:
>
> .syntax RMetaII
>
> arg = '$' <'@o.print @t'>
>     | .string <'@o.print ' $>;
>
> output = '<' *arg '>' <'@o.print "\n"'>;
>
> exp3 = .id <'compile_' $>
>      | .string <'@i.scan /\s*/; s=' $>
>        <'@f = (@i.peek(s.length) == s) ? (@t=s; @i.pos += s.length) : nil'>
>      | '.id' <'@i.scan /\s*/; @f = @t = @i.scan /[A-Za-z]+[A-Za-z0-9_]+/'>
>      | '.string' <'@i.scan /\s*/; @f = @t = @i.scan /\047[^\047]*\047/'>
>      | '(' exp1 ')'
>      | '.e' <'@f = true'>
>      | '*' <'begin'> exp3 <'end while @f'> <'@f = true'>;
>
> exp2 = ( exp3 <'if @f'> | output <'if true'> )
>        *( exp3 <'raise("error at: " + @i.rest.split("\n")[0]) if !@f'>
>         | output ) <'end'>;
>
> exp1 = <'begin'> exp2
>        *( '|' <'break if @f'> exp2 )
>        <'end while false'>;
>
> stat = .id <'def compile_' $> '=' exp1 ';' <'end'>;
>
> program = '.syntax' .id
>   <'#!/usr/bin/env ruby'> <'require "strscan"'>
>   <'class ' $> <'def compile(str, out)'>
>   <'@i, @o = StringScanner.new(str), out'>
>   <'compile_program'> <'end'>
>   *stat '.end' <'end'>
>   <'puts "Usage: meta_compiler <input> <output>" unless ARGV.length == 2'>
>   <'File.open(ARGV[1], "w") {|f| RMetaII.new.compile(File.read(ARGV[0]),
> f)}'>;
>
> .end
>
> Very little error handling and I'm sure there are bugs but it's a start...
> :)
>
> The reverse order of the rules are really just an artefact of basing it on
> Long's version. Ruby does not require a certain order so can be reformatted
> to be top-down.
>
> Found at: https://github.com/robertfeldt/meta_compile
>
> Cheers,
>
> Robert Feldt
>
>
> Diff to Long's support.h :
>
> 103c103
> < if (('A' <= source[pos] && source[pos] <= 'A') ||
> ---
> > if (('A' <= source[pos] && source[pos] <= 'Z') ||
> 112c112
> < while (('A' <= source[pos] && source[pos] <= 'A') ||
> ---
> > while (('A' <= source[pos] && source[pos] <= 'Z') ||
>
>
> _______________________________________________
> fonc mailing list
> [email protected]
> http://vpri.org/mailman/listinfo/fonc
>
>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

char *output_name = NULL;
FILE *output = NULL;
char *source = NULL;
int pos = 0;
int line = 1;
int test_flag = 0;
char *token = NULL;

void skip_whitespace(void)
{
	while (source[pos] == '\x20' || source[pos] == '\t' ||
			source[pos] == '\r' || source[pos] == '\n') {
		// increment line counter when new line reached
		if (source[pos] == '\n') {
			line++;
		}
		pos++;
	}
}

void make_token(int start_pos)
{
	int length = pos - start_pos;
	free(token);
	token = malloc(length + 1);
	token[length] = '\0';
	memcpy(token, &source[start_pos], length);
}

// emits the currently recognized token
void emit_token(void)
{
	int i;
	// strings are converted to C format
	if (token[0] == '\'') {
		fprintf(output, "\"");
		for (i = 1; token[i] != '\0' && token[i] != '\''; i++) {
			switch (token[i]) {
			case '\"':
				fprintf(output, "\\\"");
				break;
			case '\\':
				fprintf(output, "\\\\");
				break;
			default:
				fprintf(output, "%c", token[i]);
				break;
			}
		}
		fprintf(output, "\"");
		return;
	}
	// if token is not a string, emit as-is
	fprintf(output, "%s", token);
}

void emit(const char *str)
{
	fprintf(output, "%s", str);
}

void emit_nl(void)
{
	fprintf(output, "\n");
}

void read_literal(const char *literal)
{
	int entry_pos;
	int i;

	skip_whitespace();
	// compare source with the literal
	entry_pos = pos;
	i = 0;
	while (source[pos] != '\0' && literal[i] != '\0' &&
			source[pos] == literal[i]) {
		pos++;
		i++;
	}
	// if the end of the literal has been reached, comparison successful
	if (literal[i] == '\0') {
		test_flag = 1;
		make_token(entry_pos);
	} else {
		// reset position
		pos = entry_pos;
		test_flag = 0;
	}
}

void read_id(void)
{
	int entry_pos;

	skip_whitespace();
	// recognize initial alphabetic character
	entry_pos = pos;
	if (('A' <= source[pos] && source[pos] <= 'Z') ||
			('a' <= source[pos] && source[pos] <= 'z')) {
		pos++;
		test_flag = 1;
	} else {
		test_flag = 0;
		return;
	}
	// recognize alphanumeric characters
	while (('A' <= source[pos] && source[pos] <= 'Z') ||
			('a' <= source[pos] && source[pos] <= 'z') ||
			('0' <= source[pos] && source[pos] <= '9')) {
		pos++;
	}
	// recognition successful, copy into token
	make_token(entry_pos);
}

void read_number(void)
{
	int entry_pos;

	skip_whitespace();
	// recognize optional negative sign
	entry_pos = pos;
	if (source[pos] == '-') {
		pos++;
	}
	// recognize initial numeric character
	if ('0' <= source[pos] && source[pos] <= '9') {
		pos++;
		test_flag = 1;
	} else {
		test_flag = 0;
		return;
	}
	// recognize subsequent numeric characters
	while ('0' <= source[pos] && source[pos] <= '9') {
		pos++;
	}
	// recognition successful, copy into token
	make_token(entry_pos);
}

void read_string(void)
{
	int entry_pos;

	skip_whitespace();
	// recognize initial single quote
	entry_pos = pos;
	if (source[pos] == '\'') {
		pos++;
		// test_flag is not set as recognition can still fail
	} else {
		test_flag = 0;
		return;
	}

	// recognize contents
	while (source[pos] != '\0' && source[pos] != '\'') {
		// increment line counter when new line reached
		if (source[pos] == '\n') {
			line++;
		}
		pos++;
	}

	// recognize final single quote
	if (source[pos] == '\'') {
		pos++;
		test_flag = 1;
		make_token(entry_pos);
	} else if (source[pos] == '\0') {
		// reset position
		pos = entry_pos;
		test_flag = 0;
	}
}

void error_if_false(void)
{
	if (!test_flag) {
		fprintf(stderr, "error in line %i at token %s\n", line, token);
		fclose(output);
		// delete the output file
		remove(output_name);
		free(source);
		free(token);
		exit(1);
	}
}

void meta_program(void);
void meta_exp1(void);

int main(int argc, char *argv[])
{
	FILE *input;
	int length;

	if (argc < 3) {
		fprintf(stderr, "usage: meta <input> <output>\n");
		exit(1);
	}

	// open input and output
	input = fopen(argv[1], "r");
	if (input == NULL) {
		fprintf(stderr, "invalid input file\n");
		exit(1);
	}
	output_name = argv[2];
	output = fopen(output_name, "w");
	if (output == NULL) {
		fprintf(stderr, "invalid output file\n");
		exit(1);
	}
	// read entire input into source
	fseek(input, 0, SEEK_END);
	length = (int)ftell(input);
	fseek(input, 0, SEEK_SET);
	source = malloc(length + 1);
	fread(source, 1, length, input);
	source[length] = '\0';
	fclose(input);

	// initially we have empty token; token is never NULL
	token = malloc(1);
	token[0] = '\0';

	// run meta
	meta_program();
	fclose(output);
	free(source);
	free(token);
	return 0;
}

/* end */
_______________________________________________
fonc mailing list
[email protected]
http://vpri.org/mailman/listinfo/fonc

Reply via email to