From 2eaa763a2cd6f88a582f7c654ec3416bfc3646fc Mon Sep 17 00:00:00 2001 From: "Johan B.W. de Vries" Date: Sun, 9 Feb 2025 15:38:02 +0100 Subject: [PATCH] Remove skipchar, add stdinlineno / stdincolno With the new check function, this helper method doesn't make much sense to put in the standard library. To replicate the same result, we do need to expose the current line number; adding column number is a nice bonus. Also; made it a bit clearer when a check failes in it2. Also; the builtincheckfalse was not working. Also; separated out the test input file to have more data. --- 0-lang0py/lang0py.py | 61 ++++++++++++++++------------- 1-lang0py/lang0py.lang0 | 62 ++++++++++++++++++----------- 2-lang0c/lang0c.lang0 | 71 +++++++++++++++++++++++++++------- README.md | 8 +++- tests/Makefile | 16 ++++---- tests/stdinlineno.lang0 | 86 +++++++++++++++++++++++++++++++++++++++++ tests/test-input.txt | 3 ++ 7 files changed, 233 insertions(+), 74 deletions(-) create mode 100644 tests/stdinlineno.lang0 create mode 100644 tests/test-input.txt diff --git a/0-lang0py/lang0py.py b/0-lang0py/lang0py.py index adfed09..7de85d1 100644 --- a/0-lang0py/lang0py.py +++ b/0-lang0py/lang0py.py @@ -39,11 +39,6 @@ def skip(): PEEK = None -def skipchar(char): - global LINE - assert char == peek(), (LINE, char, peek()) - skip() - def emitln(data): emit(data) emit(eol) @@ -61,6 +56,11 @@ def lexident(): return word +def skipchar(char): + global LINE + assert char == peek(), (LINE, char, peek()) + skip() + def parseconststring(): skipchar(quote) emit(quote) @@ -364,33 +364,40 @@ def emitheader(): emitln("eof = chr(0)") emitln("eol = chr(10)") emitln("quote = chr(34)") - emitln("PEEK = None") - emitln("LINE = 1") + emitln("") + emitln("STDINCOLNO = 0") + emitln("STDINLINENO = 1") + emitln("STDINPEEK = None") + emitln("") + emitln("def _readchar():") + emitln(" char = sys.stdin.read(1)") + emitln(" trace('char', char)") + emitln(" if not char:") + emitln(" return eof") + emitln(" return char") emitln("") emitln("def peek():") - emitln(" global PEEK") - emitln(" if PEEK is None:") - emitln(" char = sys.stdin.read(1)") - emitln(" trace('char', char)") - emitln(" if not char:") - emitln(" PEEK = eof") - emitln(" else:") - emitln(" PEEK = char") - emitln(" return PEEK") - emitln("") - emitln("peek()") + emitln(" return STDINPEEK") emitln("") emitln("def skip():") - emitln(" global LINE") - emitln(" global PEEK") - emitln(" if eol == PEEK:") - emitln(" LINE += 1") - emitln(" PEEK = None") + emitln(" global STDINCOLNO") + emitln(" global STDINLINENO") + emitln(" global STDINPEEK") + emitln(" if eol == STDINPEEK:") + emitln(" STDINLINENO += 1") + emitln(" STDINCOLNO = 0") + emitln(" STDINCOLNO += 1") + emitln(" STDINPEEK = _readchar()") emitln("") - emitln("def skipchar(char):") - emitln(" global LINE") - emitln(" assert char == peek(), (LINE, char, peek())") - emitln(" skip()") + emitln("def stdinlineno():") + emitln(" global STDINLINENO") + emitln(" return str(STDINLINENO)") + emitln("") + emitln("def stdincolno():") + emitln(" global STDINCOLNO") + emitln(" return str(STDINCOLNO)") + emitln("") + emitln("skip()") emitln("") def emitfooter(): diff --git a/1-lang0py/lang0py.lang0 b/1-lang0py/lang0py.lang0 index aa9a897..d153d14 100644 --- a/1-lang0py/lang0py.lang0 +++ b/1-lang0py/lang0py.lang0 @@ -1,3 +1,14 @@ +skipchar exp: + declare act + declare lineno + declare colno + calc act peek + calc lineno stdinlineno + calc colno stdincolno + check eq exp act : "Unexpected character" act "expected" exp "at" lineno colno + skip +/ + emitln data: emit data emit eol @@ -430,33 +441,40 @@ emitheader: emitln "eof = chr(0)" emitln "eol = chr(10)" emitln "quote = chr(34)" - emitln "PEEK = None" - emitln "LINE = 1" + emitln "" + emitln "STDINCOLNO = 0" + emitln "STDINLINENO = 1" + emitln "STDINPEEK = None" + emitln "" + emitln "def _readchar():" + emitln " char = sys.stdin.read(1)" + emitln " trace('char', char)" + emitln " if not char:" + emitln " return eof" + emitln " return char" emitln "" emitln "def peek():" - emitln " global PEEK" - emitln " if PEEK is None:" - emitln " char = sys.stdin.read(1)" - emitln " trace('char', char)" - emitln " if not char:" - emitln " PEEK = eof" - emitln " else:" - emitln " PEEK = char" - emitln " return PEEK" - emitln "" - emitln "peek()" + emitln " return STDINPEEK" emitln "" emitln "def skip():" - emitln " global LINE" - emitln " global PEEK" - emitln " if eol == PEEK:" - emitln " LINE += 1" - emitln " PEEK = None" + emitln " global STDINCOLNO" + emitln " global STDINLINENO" + emitln " global STDINPEEK" + emitln " if eol == STDINPEEK:" + emitln " STDINLINENO += 1" + emitln " STDINCOLNO = 0" + emitln " STDINCOLNO += 1" + emitln " STDINPEEK = _readchar()" emitln "" - emitln "def skipchar(char):" - emitln " global LINE" - emitln " assert char == peek(), (LINE, char, peek())" - emitln " skip()" + emitln "def stdinlineno():" + emitln " global STDINLINENO" + emitln " return str(STDINLINENO)" + emitln "" + emitln "def stdincolno():" + emitln " global STDINCOLNO" + emitln " return str(STDINCOLNO)" + emitln "" + emitln "skip()" emitln "" / diff --git a/2-lang0c/lang0c.lang0 b/2-lang0c/lang0c.lang0 index 995ef4e..9c19c72 100644 --- a/2-lang0c/lang0c.lang0 +++ b/2-lang0c/lang0c.lang0 @@ -1,3 +1,14 @@ +skipchar exp: + declare act + declare lineno + declare colno + calc act peek + calc lineno stdinlineno + calc colno stdincolno + check eq exp act : "Unexpected character" act "expected" exp "at" lineno colno + skip +/ + emitln data: emit data emit eol @@ -273,13 +284,23 @@ parsestatcheck indent: emit indent emitln "{" + emit " fprintf(stderr, " + emit quote + emit "%s" + emit quote + emit ", " + emit quote + emit "ERROR:" + emit quote + emitln ");" + forever skipchar " " emit indent emit " fprintf(stderr, " emit quote - emit "%s " + emit " %s" emit quote emit ", " calc char peek @@ -568,35 +589,55 @@ emitheader: emitln " return 0;" emitln "}" emitln "" - emitln "int LINE = 1;" + emitln "int STDINCOLNO = 1;" + emitln "int STDINLINENO = 1;" + emitln "char * STDINPEEK = 0;" emitln "" - emitln "char * peek()" + emitln "char * _readchar()" emitln "{" emitln " char * res = malloc(2*sizeof(char));" emitln " res[0] = getc(stdin);" emitln " res[1] = 0;" - emitln " ungetc(res[0], stdin);" emitln " return res;" emitln "}" emitln "" + emitln "char * peek()" + emitln "{" + emitln " if( !STDINPEEK ) STDINPEEK = _readchar(); // First byte read" + emitln " return STDINPEEK;" + emitln "}" + emitln "" emitln "void skip()" emitln "{" - emitln " char c = getc(stdin);" - emitln " if( c == 10 ) LINE += 1;" + emitln " if( !STDINPEEK ) STDINPEEK = _readchar(); // First byte read, not even peek()'d" + emitln " if( STDINPEEK[0] == 10 ) {" + emitln " STDINLINENO += 1;" + emitln " STDINCOLNO = 0;" + emitln " }" + emitln " STDINCOLNO += 1;" + emitln " STDINPEEK = _readchar();" emitln "}" emitln "" - emitln "void skipchar(char * chr)" + emitln "char * stdinlineno()" emitln "{" - emitln " assert(strlen(chr) == 1);" - emitln " char * act = peek();" - emitln " assert(strlen(act) == 1);" - emitln " if( chr[0] == act[0] ) {skip(); return;};" - emit " fprintf(stderr, " + emitln " char * res = malloc(8*sizeof(char));" + emit " sprintf(res, " emit quote - emit "Expected '%c' on line %d but saw '%c' instead%c" + emit "%d" emit quote - emitln ", chr[0], LINE, act[0], 10);" - emitln " exit(1);" + emitln ", STDINLINENO);" + emitln " return res;" + emitln "}" + emitln "" + emitln "char * stdincolno()" + emitln "{" + emitln " char * res = malloc(8*sizeof(char));" + emit " sprintf(res, " + emit quote + emit "%d" + emit quote + emitln ", STDINCOLNO);" + emitln " return res;" emitln "}" emitln "" return diff --git a/README.md b/README.md index 082ac23..1bee0d4 100644 --- a/README.md +++ b/README.md @@ -151,9 +151,13 @@ Checks stdin for the next character and returns it. Advances stdin a single character. -#### skipchar a +#### stdincolno -Advances stdin a single character, if it matches the first character of `a`. Otherwise, exits the program. +Returns the column number for stdin (starting at 1) + +#### stdinlineno + +Returns the line number for stdin (starting at 1) ### Typing diff --git a/tests/Makefile b/tests/Makefile index 030c87c..8eb876e 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -18,18 +18,18 @@ check: all-it0.results all-it1.results all-it2.results all-it0.results: $(addprefix build/,$(addsuffix .it0, $(TESTLIST))) build/builtincheckfalse.it0 -rm -f $@ - echo "Hello" | build/builtincheckfalse.it0 2> /dev/null 2>&1 | grep 'Success' - $(foreach test,$(TESTLIST), echo -n "$(test) " >> $@; echo "Hello" | ./build/$(test).it0 >> $@ ; echo "" >> $@ ;) + cat test-input.txt | build/builtincheckfalse.it0 2> /dev/null 2>&1 | grep 'Success' + $(foreach test,$(TESTLIST), echo -n "$(test) " >> $@; cat test-input.txt | ./build/$(test).it0 >> $@ ; echo "" >> $@ ;) -all-it1.results: $(addprefix build/,$(addsuffix .it1, $(TESTLIST))) build/builtincheckfalse.it0 +all-it1.results: $(addprefix build/,$(addsuffix .it1, $(TESTLIST))) build/builtincheckfalse.it1 -rm -f $@ - echo "Hello" | build/builtincheckfalse.it0 2> /dev/null 2>&1 | grep 'Success' - $(foreach test,$(TESTLIST), echo -n "$(test) " >> $@; echo "Hello" | ./build/$(test).it1 >> $@ ; echo "" >> $@ ;) + cat test-input.txt | build/builtincheckfalse.it1 2> /dev/null 2>&1 | grep 'Success' + $(foreach test,$(TESTLIST), echo -n "$(test) " >> $@; cat test-input.txt | ./build/$(test).it1 >> $@ ; echo "" >> $@ ;) -all-it2.results: $(addprefix build/,$(addsuffix .it2, $(TESTLIST))) build/builtincheckfalse.it0 +all-it2.results: $(addprefix build/,$(addsuffix .it2, $(TESTLIST))) build/builtincheckfalse.it2 -rm -f $@ - echo "Hello" | build/builtincheckfalse.it0 2> /dev/null 2>&1 | grep 'Success' - $(foreach test,$(TESTLIST), echo -n "$(test) " >> $@; echo "Hello" | ./build/$(test).it2 >> $@ ; echo "" >> $@ ;) + cat test-input.txt | build/builtincheckfalse.it2 2> /dev/null 2>&1 | grep 'Success' + $(foreach test,$(TESTLIST), echo -n "$(test) " >> $@; cat test-input.txt | ./build/$(test).it2 >> $@ ; echo "" >> $@ ;) clean: -rm -f *.results build/*.it0* build/*.it1* build/*.it2* diff --git a/tests/stdinlineno.lang0 b/tests/stdinlineno.lang0 new file mode 100644 index 0000000..1f15ae9 --- /dev/null +++ b/tests/stdinlineno.lang0 @@ -0,0 +1,86 @@ +main: + declare char + declare colno + declare lineno + + calc char peek + calc colno stdincolno + calc lineno stdinlineno + check eq char "H" : "Read: H" char + check eq colno "1" : "The column number should not have advanced yet" colno + check eq lineno "1" : "The line number should not have advanced yet" lineno + skip + + calc char peek + calc colno stdincolno + calc lineno stdinlineno + check eq char "e" : "Read: e" char + check eq colno "2" : "The column number should have been advanced by 1" colno + check eq lineno "1" : "The line number should not have advanced yet" lineno + skip + + calc char peek + calc colno stdincolno + calc lineno stdinlineno + check eq char "l" : "Read: l" char + check eq colno "3" : "The column number should have been advanced by 2" colno + check eq lineno "1" : "The line number should not have advanced yet" lineno + skip + + calc char peek + calc colno stdincolno + calc lineno stdinlineno + check eq char "l" : "Read: l" char + check eq colno "4" : "The column number should have been advanced by 3" colno + check eq lineno "1" : "The line number should not have advanced yet" lineno + skip + + calc char peek + calc colno stdincolno + calc lineno stdinlineno + check eq char "o" : "Read: o" char + check eq colno "5" : "The column number should have been advanced by 4" colno + check eq lineno "1" : "The line number should not have advanced yet" lineno + skip + + calc char peek + calc colno stdincolno + calc lineno stdinlineno + check eq char "!" : "Read: !" char + check eq colno "6" : "The column number should have been advanced by 5" colno + check eq lineno "1" : "The line number should not have advanced yet" lineno + skip + + calc char peek + calc colno stdincolno + calc lineno stdinlineno + check eq char eol : "Read: eol" char + check eq colno "7" : "The column number should have been advanced by 6" colno + check eq lineno "1" : "The line number should not have advanced yet" lineno + skip + + calc char peek + calc colno stdincolno + calc lineno stdinlineno + check eq char eol : "Read: eol" char + check eq colno "1" : "The column number should have been reset" colno + check eq lineno "2" : "The line number should have advanced by 1" lineno + skip + + calc char peek + calc colno stdincolno + calc lineno stdinlineno + check eq char "T" : "Read: T" char + check eq colno "1" : "The column number should have been reset again" colno + check eq lineno "3" : "The line number should have advanced by 2" lineno + skip + + calc char peek + calc colno stdincolno + calc lineno stdinlineno + check eq char "h" : "Read: h" char + check eq colno "2" : "The line number should not have been advanced by 1" colno + check eq lineno "3" : "The line number should not have advanced further" lineno + + emit "Success" +/ diff --git a/tests/test-input.txt b/tests/test-input.txt new file mode 100644 index 0000000..37d8d0a --- /dev/null +++ b/tests/test-input.txt @@ -0,0 +1,3 @@ +Hello! + +This is an example of standard input.