สร้างล่ามภาษาการเขียนโปรแกรม [ปิด]


19

งาน:

คุณต้องสร้างล่ามที่สามารถแยกวิเคราะห์ตัวอย่างของภาษาการเขียนโปรแกรม ภาษาไม่จำเป็นต้องมีความซับซ้อน แต่จะต้องมีองค์ประกอบของการสร้างประโยคต่อไปนี้:

  • ความสามารถในการกำหนดและอ่านตัวแปร (อาจจะง่ายเหมือนa- zเป็นตัวแปรที่สร้างไว้ล่วงหน้า)
  • ถ้าข้อความ (อื่นและไม่จำเป็นต้องมี)
  • ลูป (นับเป็นจำนวนตามอำเภอใจไม่ต้องใช้การเข้าถึงตัวนับ)
  • คณิตศาสตร์อย่างง่ายพร้อมตัวแปร (การบวกการลบการคูณการหารการหารมากกว่า / น้อยกว่าเท่ากับ)
  • พิมพ์คำสั่ง

กฎ:

  • คุณไม่สามารถคัดลอกไวยากรณ์ของภาษายอดนิยมอื่น
  • คุณต้องเขียนล่ามของคุณเองไม่ใช่การดัดแปลงล่ามอื่น
  • คุณสามารถเขียนล่ามในภาษาใดก็ได้
  • เขียนโปรแกรมตัวอย่าง 99 ขวดเบียร์ในภาษาของคุณ (ดูที่นี่ )
  • นี่คือการดังนั้นคำตอบที่ได้รับการโหวตมากที่สุดจะเป็นผู้ชนะ

3
ข้อกำหนดของ parser คืออะไร มันจำเป็นต้องเอาทรีของไวยากรณ์ออกมาไหม?
Kendall Frey

1
ความสามารถในการผลักดันและป๊อปอัพจากสแต็กเพียงพอที่จะตอบสนองความต้องการของตัวแปรหรือไม่?
tecywiz121

1
@Dennis - จะต้องสามารถวนซ้ำตามจำนวนที่กำหนดในรันไทม์
TheDoctor

2
ฉันเชื่อว่าเป็นล่ามใช่ไหม
Kendall Frey

3
อยากเห็นกุยโด้ส่ง CPython
user2357112 รองรับ Monica

คำตอบ:


17

DogeScript

โปรแกรมเบียร์ 99 ขวด:

many amaze 99 time

such scare bottles-of-beer
such scream on-the-wall
many despair 13 time

such fail take-one-down-pass-it-around

wow

so amaze
so scare
so scream
so despair!

so amaze
so scare
so despair!

much amaze

so fail
so despair!

so amaze
so scare
so scream

so despair!
so despair!

very amaze

wow

ล่าม PHP:

<?php
$input=fopen('php://stdin', 'r');

//pre-process input
$input=preg_replace("/ +/", " ", $input); //replace any multiple spaces by a single space

//split into instructions by newlines
$instructions=explode("\n", $input);

$loopstartpoint= -1;
$variables=array();
$activevariable="";
for($instrpointer=0; $instrpointer<count($instructions); $instrpointer++)
{
    $tokens=explode(" ", $instructions[$instrpointer]);
    switch($tokens[0])
    {
        case "wow":
            if($loopstartpoint<0)
            {
                $loopstartpoint=$instrpointer+1;
            }
            else
            {
                if($variables[ $activevariable ])
                {
                    $instrpointer=$loopstartpoint;
                }
                else
                {
                    $loopstartpoint= -1;
                }
            }
            break;
        case "so":
            if(substr($tokens[1], -1)=="!")
            {
                echo chr($variables[ substr($tokens[1], 0, -1) ]);
            }
            else
            {
                echo $variables[ $tokens[1] ];
                echo " ";
            }
            break;
        case "very":
            $activevariable=$tokens[1];
            break;
        case "much":
            if(!isset($variables[ $tokens[1] ]))
                $variables[ $tokens[1] ]=0;
            if(count($tokens)==2)
            {
                $variables[ $tokens[1] ]--;
            }
            else
            {
                for($loop=0;$loop<$tokens[2];$loop++)
                {
                    $variables[ $tokens[1] ]--;
                }
            }
            $activevariable=$tokens[1];
            break;
        case "many":
            if(!isset($variables[ $tokens[1] ]))
                $variables[ $tokens[1] ]=0;
            if(count($tokens)==2)
            {
                $variables[ $tokens[1] ]++;
            }
            else
            {
                for($loop=0;$loop<$tokens[2];$loop++)
                {
                    $variables[ $tokens[1] ]++;
                }
            }
            $activevariable=$tokens[1];
            break;
        case "such":
            $variables[ $tokens[1] ]=$tokens[2];
            $activevariable=$tokens[1];
            break;
    }
}
?>

ไวยากรณ์ที่เป็นอยู่ในปัจจุบัน:

wow - start and end loops, end of loop checks if active variable is 0 and loops if not
so without ! - print variable's value
so with ! - print variable's ASCII character
much - decrement this variable
many - increment this variable
such - set variable
very - make variable active
x time - does previous statement x times

Variables are initially 0.

ลองมันนี่
ข้อเสนอแนะสำหรับการปรับปรุงยินดีต้อนรับ


2
prgramming lvl = 100
Antonio Ragagnin

1
คุณรู้หรือไม่ว่ามันมีอยู่แล้ว ?
อัล

ไม่ฉันไม่เคยเห็นเลย คนเกียจคร้านฉันคิดว่าฉันเป็นคนดั้งเดิม :-(
Gareth

10

BrainBack: ภาษาที่รวบรวมไว้เป็นกองซึ่งทำงานอยู่บน BrainFuck

NB: สเป็คก็เปลี่ยนจาก "สร้าง parser" เป็น "สร้างล่าม" หลังจากที่ฉันโพสต์คำตอบนี้ คำตอบนี้เป็นคอมไพเลอร์ซึ่งแยกวิเคราะห์รหัสที่มา

ชื่อคือปุนบนBackซึ่งตรงกันข้ามกับภาษาสแต็คที่รู้จักกันดีและสมองแสดงให้เห็นว่ามันเป็นธรรมชาติที่ลึกลับ ดูเหมือนว่า BrainFuck เล็กน้อย (แต่ไม่ใช่) แต่คอมไพเลอร์ทำงานบน BrainFuck และมันเป็นรหัสวัตถุที่คอมไพล์แล้วจบลงเมื่อไบนารี BrainFuck

ภาษา: * == ทำลายข้อโต้แย้งของมัน

  • "constant" พิมพ์คงที่
  • # พิมพ์ด้านบนของสแต็กเป็นตัวเลข
  • > ทำซ้ำด้านบนของสแต็ก
  • <num> ผลักดันจำนวนคง<num>ที่เป็นค่าไปด้านบนของสแต็ค
  • < ลบด้านบนของสแต็ค
  • - substract จากบนสุดที่สอง *
  • + เพิ่มจากบนลงล่างเป็นสูงสุด *
  • ! ไม่สลับค่าบวก / ศูนย์ *
  • [ ... ] ในขณะที่ด้านบนของสแต็กไม่เป็นศูนย์คล้ายกับ BrainFuck

เบียร์ 99 ขวดที่มีเนื้อร้องที่ถูกต้องใน BrainBack:

100
[
 1 -
 > ! [ < 0 0 "No more" ] < [ # 0 ] <
 " bottle"
 > 1 - [ < 0 "s" ] < 
 " of beer on the wall, "
 > ! [ < 0 0 "no more" ] < [ # 0 ] <
 " bottle"
 > 1 - [ < 0 "s" ] < 
 " of beer.
"
 > ! [ < 0 0 "Go to the store and buy some more, " ] < 
     [ "Take one down and pass it around, " 0 ] <

 > ! [ < 1 0 0 "99" ] < [ > 1 - > !  [ < < 1 0 0 "no more" ] < [ # 1 - 0 ] ] <
 " bottle"
 [ < 0 "s" ] <
 " of beer on the wall.

"
]

คอมไพเลอร์ BrainBack เขียนในExtended BrainFuck

;;; Macros

;; utility function that substracts 
;; ^2 from ^0 without reduceing ^2 
;; below zero. ^1 needs to be zero
{substract 
  (<<[->]>[<]>-)
}


;; Main macro is the main  program and
;; has the overal structure of the program.
;; every macro here is define in order below.
{main
  :i
  :wrk
  :tmp
  :else
  :sub 
  $i+(
  ; switch ( $wrk ) cases '"#><-+![]9;' using $tmp,$else
  $tmp+++++(-$wrk------)+$wrk---; !
  ($wrk-; "
    ($wrk-; #
      ($wrk--------; +
        ($wrk--; -
          ($wrk--- $tmp- $else 9+ &substract $tmp+ $wrk; 0-9
            ($wrk--; ;
              ($wrk-; <
                ($wrk--; >
                  ($tmp++++(-$wrk------)+$wrk+; [
                    ($wrk--; ]
                      (#(-) $tmp(-)  no matches)
                        $tmp (- #match 'cl'  &close        )
                    ) $tmp (- #match 'op'    &open         )
                  ) $tmp (- #match 'gt'      &dup          )
                ) $tmp (- #match 'lt'        &slash        )
              ) $tmp (  #match 'c'           &comment      )
            ) $tmp (- #match 0 to 9          &read_number  )
          ) $tmp (- #match 'minus'           &sub          )
        ) $tmp (- #match 'plus'              &add          )
      ) $tmp (- #match '#'                   &print_number )
    ) $tmp (- #match '"'                     &print_string )
  ) $tmp (- #match 'not'                     &not          )

  $i(-)$tmp#,+(-(-$wrk+$i+)))
  10+.
}

;; implements close bracket
{close 
    |" close"(-)
    $i.
}

;; implements open bracket
{open 
    |" open"(-)
    $i.
}

;; implements dup/>
{dup
    |"dup [->>+<<]>>[-<+<+>>]<
"
     (-)
}

;; implements slash/<
{slash
     |"slash [-]<
"
     (-)
}

;; implements comment
{comment
  [,10-]
}

;; implements read_number/<number>
;; makes code that if run makes 
;; the constant
{read_number
  ;TODO: compiler_read_constant_number
   $wrk|"number"(-)
#  $wrk 6+ (- $i 8-)
  ~"+>"<.(-)
  $i+(-(-$wrk.)
     #$else, $tmp 6+ (- $else 8-)
     $else(-$tmp+$i+)
     $sub 9+ &substract
     $else+
     $tmp((-) $i(-) $else-)
     $else(-|"[->++++++++++<]>[-<+>]<"(-)$i+)
     )
   $wrk(-)
   |"
"(-)
}

;; implements sub/-
{sub
     |"sub [-<->]<
"
     (-)
}

;; implements add/+
{add
     |"#add [-<+>]<
"
     (-)
}

;; implements print_number/#
{print_number
  |"print [->+<]>[-<+>>+<]>
    [>++++++++++<
    [->-[>+>>]>[+[-<+>]>+>>]<<<<<]
    +>[-]>[-<<+>>]>[-<<+>>]<<]
    +<[>-<[<]]>[>]
    <[>++++++[-<++++++++>]<-.[-]<]<
"(-)

}

;; implements print_string/"..."
;; this outputs EBF code making the
;; object code EBF
{print_string
  |"print >|"(-) 
  $i(-$wrk+$else+)
  $wrk($tmp(-$wrk+)$wrk.,$else(-$tmp+$wrk-$i+)$i(-$else+))
  $tmp(-)$else.(-)|"[-]<
"(-)
}

;; implements not/!
;; creates code that negates top of stack
{not
  |"not >+<[[-]>-]>[<+>->]<<
"(-)
}

&main

ในการรวบรวม BrainBack:

bf ebf.bf < BrainBack.ebf > BrainBack.bf

ในการรวบรวมโปรแกรม BrainBack:

bf BrainBack.bf < 99.bb > 99.ebf # compile from bb to ebf
bf ebf.bf < 99.ebf > 99.bf       # compile from ebf to bf 

รันไบนารี:

bf 99.bf                        

ที่นี่ฉันใช้bfซึ่งมีอยู่ในส่วนใหญ่ debian beefและผู้อื่นสามารถใช้เช่นกัน ทั้งคอมไพเลอร์ EBF, BrainBack และรหัสวัตถุจะเข้ากันได้กับ BrainFuck ไบนารี

มันควรจะขยายเพื่อพิมพ์เซลล์เป็น ascii .สามารถอ่านไบต์ใน,และมีswapการดำเนินการต่าง ๆให้มีประโยชน์มากขึ้น จำเป็นอย่างยิ่งในการสร้างคอมไพเลอร์ BrainBack หรือล่ามใน BrainBack


6
ว้าว! คุณอยู่ที่ 1,337 คะแนนตัวแทนตอนนี้ ... เกือบจะไม่ต้องการ upvote และทำให้เสียหมายเลข! :)
luser droog

6

ฉันใช้เวลาส่วนใหญ่กับสคริปต์ PHP และทำให้ฉันมีคำถาม: เพราะเหตุใดฉันจึงถูกบังคับให้ใช้$ชื่อตัวแปรของฉัน เป็นสกุลเงินท้องถิ่นของฉันดังนั้นลองใช้ดู! เนื่องจากมีการใช้€ในหลายประเทศฉันจึงใช้คำบางคำจากภาษา EU เป็นคำหลัก

€beers gleich 99
€bottles gleich bottles of beer
€bottles_on_the_wall gleich bottles of beer on the wall

mientras €beers topogleich 3
    afficher €beers €bottles_on_the_wall
    afficher , €beers €bottles
    afficher . NETHERLANDS
    odejmowanie €beers

    afficher Take one down and pass it around, €beers
    afficher €bottles_on_the_wall
    afficher . NETHERLANDS NETHERLANDS
sartneim

afficher 2 bottles of beer on the wall, 2 bottles of beer. NETHERLANDS
afficher Take one down and pass it around, 1 bottle of beer on the wall.
afficher NETHERLANDS NETHERLANDS

afficher 1 bottle of beer on the wall, 1 bottle of beer. NETHERLANDS
afficher Take one down and pass it around, no more bottles of beer on the wall.
afficher NETHERLANDS NETHERLANDS

afficher No more bottles of beer on the wall, no more bottles of beer. NETHERLANDS
afficher Go to the store and buy some more, 99 bottles of beer on the wall.
afficher NETHERLANDS NETHERLANDS

คำสำคัญ:

  • gleichมีค่าเท่ากันในภาษาเยอรมัน
  • mientrasคือในขณะที่ในภาษาสเปน
  • topoมีมากขึ้นในภาษาโปรตุเกส (อัปเดต: ควรเป็นmaiorแทนขอบคุณdaHugLennyสำหรับคำแนะนำ)
  • odejmowanieจะถูกลบในโปแลนด์
  • afficherพิมพ์เป็นภาษาฝรั่งเศส
  • มีการขึ้นบรรทัดใหม่nlบางครั้งและ TLD ของNETHERLANDSคือnlดังนั้นฉันจึงกำหนดค่าคงที่NETHERLANDSเพื่อแสดงบรรทัดใหม่

ฉันโกงนิดหน่อยตั้งแต่ไม่มี ifคำหลักฉันเลือกพิมพ์สองบรรทัดสุดท้ายโดยตรง

ล่ามใน Python

ล่ามจะไม่ทำอะไรมากไปกว่าเรียกใช้สคริปต์เพื่อแสดงเบียร์ 99 ขวด

# -*- coding: utf-8 -*-
# @see http://stackoverflow.com/questions/12655836/writing-an-xml-file-that-contains-a-euro-symbol-in-python-using-xml-etree/12655861#12655861

# =             gleich (german)
# while         mientras (spanish)
# >             topo (portuguese) (it should be "maior" instead)
# subtract      odejmowanie (polish
# print         afficher (french)
# newline       NETHERLANDS

import sys, codecs

class euro:
    symbols = {}
    sign = u'€'

    def executeLine(self, line):
        s = line.split(' ')

        if s[0] == 'afficher':
            buffer = []

            for a in s[1:]:
                if (a == ''):
                    continue
                elif (a[0] == self.sign):
                    buffer.append(str(self.getSymbol(a)))
                elif (a == 'NETHERLANDS'):
                    buffer.append("\n")
                else :
                    buffer.append(a)

            sys.stdout.write(' '.join(buffer))
            # @see http://stackoverflow.com/questions/4499073/printing-without-newline-print-a-prints-a-space-how-to-remove/4499172#4499172
        elif s[0] == 'odejmowanie':
            self.setSymbol(s[1], (int(self.getSymbol(s[1])) - 1))
        elif (len(s) >= 3) and (s[1] == 'gleich'):
            self.setSymbol(s[0], (' ').join(s[2:]))

    def executeBlock(self, lines, statement):
        while (self.getStatement(statement)):
            for line in lines:
                self.executeLine(line)

    def getStatement(self, statement):
        if (statement[1] == 'topogleich'):
            return self.getSymbol(statement[0]) >= int(statement[2])

    def setSymbol(self, name, value):
        name = self.withoutEuro(name)
        self.symbols[name] = value

    def getSymbol(self, name):
        #~ print symbols, withoutEuro(name)
        name = self.withoutEuro(name)
        if name in self.symbols:
            value = self.symbols[name]

            return value
        else :
            print "\n-----\n",'Error: "', name, '"is not in', self.symbols, '-----'

            #~ sys.exit()

    def withoutEuro(self, string):
        return(string.replace(self.sign, ''))

    def parseFile(self, f):
        linesStack = []

        for line in codecs.open(f, 'r', 'utf-8'):
            line = line.replace('\n', '').replace('\t', '')
            s = line.split(' ')

            if (len(s) == 1) & (s[0] == '') :
                continue

            if (s[0] == 'mientras'):
                statement = s[1:]

                linesStack.append(line)
            elif (s[0] == 'sartneim'):
                linesStack.append(line)

                self.executeBlock(linesStack, statement)

                linesStack = []
                statement = ''
            elif (len(linesStack) > 0):
                linesStack.append(line)
            else:
                self.executeLine(line)

euro = euro()
euro.parseFile(sys.argv[1])

หากต้องการเรียกใช้ให้บันทึกไฟล์ทั้งสองจากนั้นเรียกใช้ไฟล์ Python พร้อม.euสคริปต์เป็นอาร์กิวเมนต์:

python euro.py euro.eu

4
+1! แจ้งเตือนฉันเกี่ยวกับ 'If PHP Were British': addbytes.com/blog/if-php-were-british
Pieter Witvoet

ภาษานี้ได้รับการรับรองในEsolang
อัล

1
topoอยู่ในอันดับต้น ๆในภาษาโปรตุเกส
acrolith

@daHugLenny ฉันขอโทษสำหรับความผิดพลาด คือMaiorแปลที่ถูกต้องของมากขึ้น ?
อัล

1
@AL ใช่ ( ความคิดเห็นต้องมีอย่างน้อย 15 ตัวอักษร )
acrolith

5

1Lang

1Lang เป็นภาษาคำนำหน้าฟังก์ชั่นเช่น LISP หรือ Scheme แต่ไม่มีวงเล็บที่ทำให้อ่านยากขึ้นเมื่อลบ white-space ที่ไม่จำเป็นออกไป สามารถลบวงเล็บได้เนื่องจากฟังก์ชันและตัวดำเนินการทั้งหมดใช้พารามิเตอร์จำนวนที่ทราบ

การจัดฟันต้องใช้เพื่อกำหนดขอบเขตของฟังก์ชันและผลลัพธ์ตามเงื่อนไขและบล็อกโค้ดสำรองซึ่งอาจประกอบด้วยรายการของข้อความสั่ง

ใน LISP แฟคทอเรียลอาจถูกกำหนดดังนี้:

(defun fact (x) (if (< x 2) 1 (* x (fact (- x 1))) ) )

ใน 1Lang นี้จะเป็น

@Fx{ ? < x 2 {1} {* x F -x1} }

ซึ่งสามารถลดลงได้

@Fx{?<x2{1}{*xF-x1}}

1Lang ในปัจจุบันไม่สนับสนุนผลข้างเคียง

1Lang เขียนเป็น bash ดังนั้นในปัจจุบันจึงมีการแชร์ข้อ จำกัด ของ bash เช่นช่วงจำนวนเต็ม

a-z are variables. Variable are either integers, strings, or lists.

หมายเหตุ: รายการไม่ได้ดำเนินการอย่างเต็มที่

A-Z are functions

จำนวนเต็มเป็น bash จำนวนเต็ม (สูงสุด -2 ^ 32 ถึง 2 ^ 31-1 ฉันคิดว่า) ไม่สามารถใช้ตัวเลขติดลบได้โดยตรง หากต้องการป้อนค่าลบให้ลบออกจากศูนย์ เช่น. -5 จะถูกป้อนเป็น -0 5. ข้อ จำกัด นี้เป็นเพราะ 1Lang เป็นงานที่อยู่ระหว่างดำเนินการและไม่ต้องการจำนวนลบสำหรับแอปพลิเคชันนี้ ฉันกำลังพิจารณาใช้ ~ เป็นโอเปอเรเตอร์เชิงลบซึ่งจะอนุญาตให้ -5 ป้อนเป็น ~ 5

พื้นที่สีขาวเป็นสิ่งจำเป็นในการวิเคราะห์จำนวนเต็ม เช่น. +2 3

: means assign                                    eg. :c34 to assign 34 to c
+-*/% are binary integer operators                eg. +12 34
&|^ are binary bit-wise operators
! is unary boolean not
~ is unary one's complement
? is a if-then-else function-like operator.       eg. ?=x3{*xx}{0} is x=3 return x*x else 0
+ is also a binary string concatenation operator  eg. +99" bottles"
* is also a string repetition operator            eg. *5" hello" or *" hello"5
@ defines a function                              eg. @Fx{?<x1{1}{*xF-x1}}

ชื่อพารามิเตอร์ฟังก์ชั่นอาจโหลดตัวแปรผู้โทรมากเกินไป ตัวแปรทั้งหมดที่กำหนดไว้ในฟังก์ชั่นเป็นท้องถิ่น

ไม่จำเป็นต้องทำการพิมพ์ (แม้ว่ามันจะมีประโยชน์) เพราะ LISP เหมือนทุกคำสั่งจะส่งคืนค่าและพิมพ์ค่าสุดท้ายที่ส่งคืน

eg. +2 3 prints 5

พฤติกรรมที่ไม่คาดคิดของสัญกรณ์คำนำหน้าโดยไม่มีวงเล็บคือการต่อสตริงสามารถเขียนได้ง่าย สมมติว่าคุณต้องการต่อกัน"a" " quick" " brown" " fox"คุณอาจเขียน:

+++"a"" quick"" brown"" fox"

แต่วิธีที่ง่ายกว่าอ่านง่ายและมีข้อผิดพลาดน้อยลงคือ:

+"a"+" quick"+" brown"" fox" (Note missing + between last terms)

หรือ

+"a"+" quick"+" brown"+" fox"""

รหัสเบียร์ 99 ขวด:

:b" of beer"
:w" on the wall"
:t"Take one down and pass it around, "
:s"Go to the store and buy some more, "
:c", "
:n".\n"
@Bx{?=x0{+"No more bottles"b}{+x+" bottle"+?=x1{""}{"s"}b}}
@Fx{?=x0{+B0+w+c+B0+n+s+B99+wn}{+Bx+w+c+Bx+n+t+B-x1+w+n+"\n"F-x1}}
F99

ฟังก์ชัน B ส่งคืน "No ขวดเพิ่มเติม" หรือ "1 ขวด" หรือ "ขวด" ขึ้นอยู่กับ x

ฟังก์ชั่น F คืนค่าโองการปกติหรือข้อสุดท้าย ข้อต่อปกติถูกต่อกันด้วยข้อต่อไปนี้โดยเรียก F ซ้ำด้วย -x1 เมื่อ x เป็น 0 ฟังก์ชัน F จะส่งกลับข้อสุดท้าย

สิ่งนี้สร้างขึ้น (สำหรับความหมาย F5 เริ่มที่เบียร์ 5 ขวด ... ):

> F5
5 bottles of beer on the wall, 5 bottles of beer.
Take one down and pass it around, 4 bottles of beer on the wall.

4 bottles of beer on the wall, 4 bottles of beer.
Take one down and pass it around, 3 bottles of beer on the wall.

3 bottles of beer on the wall, 3 bottles of beer.
Take one down and pass it around, 2 bottles of beer on the wall.

2 bottles of beer on the wall, 2 bottles of beer.
Take one down and pass it around, 1 bottle of beer on the wall.

1 bottle of beer on the wall, 1 bottle of beer.
Take one down and pass it around, No more bottles of beer on the wall.

No more bottles of beer on the wall, No more bottles of beer.
Go to the store and buy some more, 99 bottles of beer on the wall.
<End>

1Lang interpreter (เขียนเป็น bash) ใน 500 บรรทัด

#!/bin/bash

LC_ALL=C  # else [a-z] and [A-Z] misbehave

# functions return result on stdout
# functions have an environment

# Requirements:
# * minimise size
#   -> eliminate delimiters
#   -> single letter variables and functions
#   -> no precidence
#   -> no overloading
# * 

# string "text with \characters as per printf"
# numbers 123
# functions F3
# Built-ins +-*/%^ &|~ ! etc.
# assignment :v12 :v"string"

log(){  local m="${l:p}" m="${m//[$NL]/\n}" v="${FUNCNAME[1]}"; echo "$v: l=[${l//[$NL]/\n}] ch=[${ch/[$NL]/\n}] next=[$m]" >&2; }
logr(){ local m="${l:p}" m="${m//[$NL]/\n}" v="${FUNCNAME[1]}"; echo "$v: l=[${l//[$NL]/\n}] ch=[${ch/[$NL]/\n}] next=[$m] ret=[${ret//[$NL]/\n}]" >&2; }
logv(){ local        v="${FUNCNAME[1]}"; echo "$v: ret=[${ret//[$NL]/\n}]" >&2; }
logm(){ local m="$1" v="${FUNCNAME[1]}"; echo "$v: ${m//[$NL]/\n} in [${read//[$NL]/\n}]." >&2; }

msg(){ echo -En "$1" >&2; }
msn(){ echo -E  "$1" >&2; }

# ==========
# Line layer
# ==========

declare l
readline(){ read -rp"1lang> " l; }

#==================
# Environment Layer
#==================

declare -A v t  # variables and variable type
declare ret typ  # all bash function return these values

# assign = : var expression
assign(){
  local var
  readch
  var && var=$ret || { logm "ERROR: variable name expected"      ; return 1; }
  exp             || { logm "ERROR: value or expression expected"; return 1; }
  v["$var"]="$ret"
  t["$var"]="$typ"
}

# get variable value
get(){
  local var
  var && var=$ret || { logm "ERROR: variable name expected"; return 1; }
  ret=${v["$var"]}
  typ=${t["$var"]}
}

declare -A func fpar 
declare -iA fnum                 # functions
# define = @ F param* { body } 
define(){
  local fn par body
  readch
  fn && fn=$ret || { logm "ERROR: function name expected"; return 1; }
  fpar[$fn]=                     # zero parameters
  fnum[$fn]=                     # zero parameter counter
  while var;do                   # read parameters
    fpar[$fn]+=$ret
    fnum[$fn]+=1                 # cound parameters
  done
  # get body but remove block delimiters
  skip "{" "}" && body="${ret:1: -1}" || { logm "ERROR: function body expected"; return 1; }
  readch                         # skip }
  func[$fn]="$body"              # store function body
  ret="@$fn${fpar[$fn]}{$body}"
  typ='f'
}

apply(){
  local fn=$ch n c s; local -i N q
  readch
  N=${fnum[$fn]}   # number of parameters
  n=${fpar[$fn]}   # parameters
  s=${func[$fn]}   # function body
  c=
  for((q=0; q<N; q++)){
    exp || { logm "ERROR: value expected"; return 1; }  
    c+="v[${n:q:1}]=\"$ret\"; "  # add value to script
    c+="t[${n:q:1}]=\"$typ\"; "  # add type to script
  }
  # parse function in a subshell and echo result and type back 
  # subshell means all variable changes in function are local
  c+="parse <<<'$s'; echo -E \"\$typ\$ret\""  # combine type and value
  ret=
  typ=
  ret="$( eval "$c" )" || { logm "ERROR: function application failed"; return 1; }
  typ="${ret::1}"  # extract type
  ret="${ret:1}"   # get actual return value
}

# bash oddities:

# [[ 1 -eq 1 ]] -> 0 or success
# [[ 1 -eq 2 ]] -> 1 or failed

# x=1\<2 -> a=1 (true)
# x=1\<1 -> a=0 (false)

# ((1==1)) -> 0 or success
# ((1==2)) -> 1 or failed

# declare -i a; a=1==1 -> a=1 (true)
# declare -i a; a=1==2 -> a=0  (false)

binary(){
  local -i iret; local op=$ch a b at bt
  readch
  exp && { a="$ret"; at=$typ; } || { logm "ERROR: initial expression expected"; return 1; }
  exp && { b="$ret"; bt=$typ; } || { logm "ERROR: second expression expected"  ; return 1; }
  ret=
  typ=
  case "$at$bt" in
    nn)  # num op num
      case "$op" in
        [\*]) iret=a*b;;
        [\^]) iret=a**b;;
        [\+]) iret=a+b;;
        [\-]) iret=a-b;;
        [\/]) [[ b -ne 0 ]] && { iret=a/b; } || { logm "ERROR: division by 0"       ; return 1; };;
        [\%]) [[ b -ne 0 ]] && { iret=a%b; } || { logm "ERROR: modulo division by 0"; return 1; };;
        [\&]) iret=a\&b;;
        [\|]) iret=a\|b;;
        [\#]) iret=a\^b;;
        [\=]) iret=a==b;;
        [\<]) iret=a\<b;;
        [\>]) iret=a\>b;;
      esac
      ret=$iret
      typ='n';;  # result is always a decimal number
    ss)  # string op string
      case "$op" in
#        [\*]) arith=a*b;;  # combine?
#        [\#]) arith=${}a**b; type='s';;
        [\+]) ret="$a$b"; typ='s';;  # concatenate
        [\-]) ret="${a//$b}"; typ='s';;  # remove substrings
        [\=]) [[ $a = $b ]]; ret=$?; typ='n';;
        [\<]) [[ $a < $b ]]; ret=$?; typ='n';;
        [\>]) [[ $a > $b ]]; ret=$?; typ='n';;
      esac;;
    ns)  # num op string  =3"hello"  ="hello"3  ="3"3  =3"4"
      case "$op" in
        [\+]) ret="$a$b"; typ='s';;  # concatenate
        [\*]) ret=$(eval echo \"\${b[0]\"{1..$a}\"}\"); typ='s';;  # repeat b a times
        [\=]) ((${#b}==a)); ret=$?; typ='n';;  # length b is a
#        [\<]) [[ $a < $b ]]; arith=$?; typ='n';;
#        [\>]) [[ $a > $b ]]; arith=$?; typ='n';;
      esac;;
    sn)  # string op num  *"hello"3  ="3"3  =3"4"
      case "$op" in
        [\+]) ret="$a$b"; typ='s';;  # concatenate
        [\*]) ret=$(eval echo \"\${a[0]\"{1..$b}\"}\"); typ='s';;  # repeat a b times
        [\=]) ((${#a}==b)); ret=$?; typ='n';;  # length a is b
#        [\<]) [[ $a < $b ]]; arith=$?; typ='n';;
#        [\>]) [[ $a > $b ]]; arith=$?; typ='n';;
      esac;;
    *) logm "ERROR: undefined operation [$op] for [$a] [$at] and [$b] [$bt]"; return 1;
  esac
  return 0
}

# FIXME: string ops?
unary(){
  local -i iret; local op="$ch"
  readch
  exp || { logm "ERROR: expression expected"; return 1; }
  case "$op" in
    [\!]) iret=\!ret;;
    [\~]) iret=\~ret;;
  esac
  ret=$iret
  typ='n'  # result is always a decimal number
}

#==============
# Control Layer
#==============

# iff = ? boolean { consequence block } { alternative block }
# ?<1 2{+4 5}{+1 2}
iff(){
  local -i c; local iff ift
  readch
  exp && c=$ret || { logm "ERROR: value or expression expected"; return 1; }
  [[ c -eq 1 ]] && {  # true so do consequence
    ws
    block && { iff="$ret"; ift="$typ"; } || { logm "ERROR: consequence block error"; return 1; }
    ws
    skip "{" "}" || { logm "ERROR: alternate block expected"; return 1; }
    ret="$iff"
    typ="$ift"
  } || {
    ws
    skip "{" "}" || { logm "ERROR: consequence block expected"; return 1; }
    ws
    block || { logm "ERROR: alternate block error"; return 1; }
  }
}

#==============
# Symbols Layer
#==============

# fn = [A-Z]
fn(){
# FIXME: make evalu?
  [[ $ch = [A-Z] ]] || return 1
  ret=$ch
  typ='c'
  readch
}

# var = [a-z]
var(){
# FIXME: make evalu?
  [[ $ch = [a-z] ]] || return 1
  ret=$ch
  typ='c'
  readch
}

# list = ( token* )
# FIXME: not finished and no operators support lists
list(){
  local list=$ch prev
  readch
  while [[ $ch != ')' ]];do
    exp || { logm "ERROR: expression expected"; return 1; }
    case $typ in
      [n]) list+=" $ret";;
      [s]) list+="$ret";;
      [l]) list+="$ret";;
    esac
    ws
  done
  ret="$list$ch"
  readch
  typ='l'
  return 0
}

#============
# Token Layer
#============

# char = ' echoch
#echoch = \ {special echo escape character} | {char}
char(){
  readch
  case "$ch" in
    [\\]) escch || { logm "ERROR: escape character expected"; return 1; };;
       ?) ret="$ch"; readch
  esac
  typ='c'
}

# escaped characters are a pain
# use read with -r to read in verbatim - no escaping
# use echo -E to write out verbatim (except \\ may be processed)

declare escchS
declare ECHO='abefnrtv'
# double \\ for a \
escch(){
  local ESC="$ch"
  readch    # skip \
  case "$ch" in
    [$ECHO])                   printf -v ret "%b" "$ESC$ch"; readch;;
       [\\]) ret="\\"; readch;;
       [\"]) ret="\""; readch;;
      [0-7])         onum && { printf -v ret "%b" "$ESC$ret"   ; } || { logm "ERROR: octal number expected"; return 1; };;
       [xU]) readch; hnum && { printf -v ret "%b" "${ESC}x$ret"; } || { logm "ERROR: hex number expected"  ; return 1; };;
          ?) ret="$ch"
             [[ $escchS ]] || {
               tidyReadCh
               logm "WARNING: only octal, hex, unicode, and [$ECHO\\\"] characters need to be escaped with '$ESC'"
               logm "WARNING: [$ch] in [$l] does not need to be escaped"
               escchS="OFF"
             }
             readch
  esac
  typ='c'
}

#  num =  digit  digit*
# onum = odigit odigit*
# onum = hdigit hdigit*

num(){  local num; num=$ch; readch; while  digit;do num+=$ret; done; ret=$num; typ='n'; }
onum(){ local num; num=$ch; readch; while odigit;do num+=$ret; done; ret=$num; typ='n'; }
hnum(){ local num; num=$ch; readch; while hdigit;do num+=$ret; done; ret=$num; typ='n'; }

#  digit = [0-9]
# odigit = [0-7]
# odigit = [0-9a-fA-F]
digit(){  [[ $ch == [0-9]       ]] || { ret=-1; return 1; }; ret=$ch; typ='s'; readch; }
odigit(){ [[ $ch == [0-7]       ]] || { ret=-1; return 1; }; ret=$ch; typ='s'; readch; }
hdigit(){ [[ $ch == [0-9a-fA-F] ]] || { ret=-1; return 1; }; ret=$ch; typ='s'; readch; }

# string = " char* "
# char = escch | {any character}
string(){
  skip "\"" "\"" || { logm "ERROR: quoted string expected"; return 1; }
  ret="${ret:1: -1}"
  typ='s'
  return 0
}

# ==========
# Char layer
# ==========

declare ch read
declare -i p L COUNT
readch(){
  if [[ p -eq L ]]; then  # need more code
    readline || { ch=; p=L=0; l="EOF"; return 1; }
    l+=$NL;
    p=0
    L=${#l}
  fi
# FIXME: remove once eady - prevents bash consuming all memory  
  COUNT+=1
  ((COUNT>100000)) && { logm "FAILSAFE: too many charcters read"; return 1; }
  ch="${l:p:1}"
  read+="$ch"
  p+=1  # queue next character
}

# skip = SS content* ES
# content = ch | escch | skip(SS ES)
# string = " ch* "
skip(){
  local s="$1" e="$2" b="$ch"
  typ='z'                    # code fragment
  [[ $ch != $s ]] && return  # nothing to skip
  readch
  while [[ -n $ch ]];do
    case "$ch" in
        $e)                 b+="$e"  ; readch; ret="$b"; return 0;;
        $s) skip "$s" "$e"; b+="$ret";;
      [\\]) escch         ; b+="$ret";;
      [\"]) skip "\"" "\""; b+="$ret";;
         ?)                 b+="$ch" ; readch
    esac
  done
  ret="$b"
  logm "ERROR: unexpected EOF"
  exit 1
}

# FIXME: still required?
shopt -s extglob
shopt -u nocasematch

declare NL; printf -v NL "%b" "\n"                 # echo $NL | hexdump -C
declare WS; printf -v WS "%b" " \n\t\r"            # define whitespace

# FIXME: should it set ret and typ? 
ws(){ while [[ $ch == [$WS] ]];do readch; done; }  # skip any WS

#=====
# eval
#=====

# exp = [0-9] num
#       | " string "
#       | : assignment
#       | @ function definition
#       | [-+*/%^] binary operation
#       | [&|#<>=] boolean operation
#       | [!~] unary operation
#       | [A-Z] function application
#       | [a-z] variable
#       | ? if expression
#       | { expression* } block expression
#       | ( expression* ) list of expressions

# spare prefix characters [ '$[]_\;, ]
# [v  head of list
# ]v tail of list

exp(){
  ws
  case "$ch" in
              [0-9]) num    || { logm "ERROR: number expected"               ; return 1; };;
#               [\']) char   || { logm "ERROR: char expected"                 ; return 1; };;
               [\"]) string || { logm "ERROR: string expected"               ; return 1; };;
               [\:]) assign || { logm "ERROR: assignment expected"           ; return 1; };;
               [\@]) define || { logm "ERROR: function definition expected"  ; return 1; };;
           [-+*/%^]) binary || { logm "ERROR: binary expression expected"    ; return 1; };;
       [\&\|#\<\>=]) binary || { logm "ERROR: binary expression expected"    ; return 1; };;
              [\!~]) unary  || { logm "ERROR: unary expression expected"     ; return 1; };;
              [A-Z]) apply  || { logm "ERROR: function failed"               ; return 1; };;
              [a-z]) get    || { logm "ERROR: variable name expected"        ; return 1; };;
               [\?]) iff    || { logm "ERROR: boolean expression expected"   ; return 1; };;
               [\{]) block  || { logm "ERROR: code block expected"           ; return 1; };;
               [\(]) list   || { logm "ERROR: list expected"                 ; return 1; };;
                 '') ret=;       logm "ERROR: unexpected EOF"                ; return 1;;
                  *) ret="$ch"                                               ; return 1;;
  esac
  return 0
}

# block = { code }
block(){
  readch                         # skip {
  while [[ $ch != "}" ]];do
    exp || { 
      tidyReadCh
      logm "WARNING: ignoring previous error or unknown symbol [$ch]"
      [[ errors+=1 -gt 5 ]] && { logm "ERROR: exiting due to too many warnings"; exit 1; }
    }
    ws
  done
  readch    # skip }
  return 0
}

#=====
# repl
#=====

# pass an expression on stdin- not used withing same ebvironment - called by apply
parse(){
  p=L  # force readline
  ch=
  read=
  readch  # clears ch
  while [[ $ch && $ch != '.' ]];do
    exp || { logm "ERROR: expression expected"; return 1; }
    read=$ch
    ws
  done
# last expression is returned as result
}

tidyReadCh(){
  tidyRead
  ch="${ch//[$NL]/\n}"
}
tidyRead(){
  read="${read//[$NL]}"
}

# repl = eval* EOF
# eval = evalu | readch
repl(){
  readch
  while [[ $ch && $ch != '.' ]];do
    exp && {
      tidyRead
      msn "> $read"  # echo line except for WS
#      echo -E "$ret [$typ]"
      echo -E "$ret"
      read=$ch
    } || {
      tidyReadCh
      msn "> $read"
      logm "WARNING: ignoring previous error or unknown symbol [$ch]"
      read=
      readch
      [[ errors+=1 -gt 5 ]] && { logm "ERROR: exiting due to too many warnings"; exit 1; }
    }
    ws
  done
  msn "<End>"
}

#=====
# test
#=====
# FIXME: negative numbers

msn "1Lang"

repl <<<'
:b" of beer"
:w" on the wall"
:t"Take one down and pass it around, "
:s"Go to the store and buy some more, "
:c", "
:n".\n"
@Bx{?=x0{+"No more bottles"b}{+x+" bottle"+?=x1{""}{"s"}b}}
@Fx{?=x0{+B0+w+c+B0+n+s+B99+wn}{+Bx+w+c+Bx+n+t+B-x1+w+n+"\n"F-x1}}
F99
'

ฉันจะรักมันมากขึ้นถ้า@Mfxy{fxy}M+3 4ทำงาน แต่แล้วคุณต้องเข้าร่วมฟังก์ชั่นและตัวแปรเนมสเปซ ใช้เวลาสักครู่เพื่อคำนวณ 99 เบียร์: p
Sylwester

@Sylwester ขอขอบคุณสำหรับความสนใจของคุณ ฉันดีใจที่คุณลองและก็ใช้งานได้ ใช่ทุบตีช้าและใช้ทุบตีตีความภาษาอื่น (โดยเฉพาะการใช้ subshells) เป็นที่น่าสนใจ แต่ไม่มีประโยชน์ M + 3 4 จะไม่ถูกต้องเนื่องจากจะมีการประเมิน +3 4 ก่อนเป็นอันดับแรก M \ xy {+ xy} 3 4 อาจเป็นไวยากรณ์ที่ใช้งานได้
philcolbourn

อืมม ดูเหมือนว่าคำอธิบายของฉันได้เรียกใช้กฎภาษามาร์กอัปและบิตหายไป
philcolbourn

ใช่เพื่อที่ว่าเมื่อconsคุณสามารถทำได้map M\x{*x2}C1C2C3C4/ => (2 4 6 8)
Sylwester

4

ครึ่งหนึ่ง (ล่าม / ผู้แปลใน Windows Batch)

ฉันไม่รู้ว่าทำไมฉันตอบปริศนาจำนวนมากในชุด windows ด้วยเหตุผลบางอย่างฉันคิดว่าฉันสนุกกับมัน: P Anyways นี่คล้ายกับสิ่งที่ฉันทำงานเพื่อความสนุกสนานในขณะที่ภาษาพื้นฐานที่ แปลเป็นแบตช์ windows โดยสคริปต์ที่เขียนด้วยแบตช์ windows มันไม่ได้น่าประหลาดใจโดยเฉพาะ แต่มันได้ผล

เบียร์ 99 ขวด

# Initialize variables
bottles ~ 99
# You can't directly compare a literal value
zero ~ 0

# This makes a point 'loop' that can be jumped to or used as a subroutine
mark loop
    write $ bottles
# You only need quotes when you have leading or trailing spaces
    print ~ " bottles of beer on the wall,"
    write $ bottles
    print ~ " bottles of beer."
    print ~ Take one down and pass it around,
    bottles @ bottles-1
    if
    bottles equ zero
        jump none
    endif
    write $ bottles
    print ~ " bottles of beer on the wall."
    print ~
jump loop

mark none
    print ~ no more bottles of beer on the wall.
    print ~
    print ~ No more bottles of beer on the wall,
    print ~ No more bottles of beer.
    print ~ Go to the store and buy some more,
    print ~ 99 bottles of beer on the wall.

วากยสัมพันธ์

มีการรับรู้โทเค็นเพียงสามเท่านั้นในแต่ละบรรทัดคั่นด้วยช่องว่าง

# คือความคิดเห็น

ในกรณีส่วนใหญ่ที่จำเป็นต้องใช้ค่า a $ในโทเค็นที่สองหมายถึงสิ่งที่สามควรถูกใช้เป็นชื่อตัวแปรในขณะที่ a ~หมายถึงค่าตัวอักษร คำแนะนำทั่วไปใช้แบบฟอร์ม<instruction> [$~] <name>คำแนะนำทั่วไปใช้แบบฟอร์มการตั้งค่าตัวแปรใช้รูปแบบเดียวกัน แต่จะถูกนำไปใช้เมื่อใดก็ตามที่ไม่รู้จัก

คำสั่งที่กำหนด:

  • printและwriteทั้งสองเอาต์พุตการเขียน แต่writeไม่เพิ่มบรรทัดใหม่ ต้องการ $ หรือ ~
  • mark สร้างจุดที่สามารถข้ามไปหรือเรียกว่าเป็นรูทีนย่อย
  • jump เทียบเท่ากับ goto in batch (หรือภาษาใด ๆ สำหรับเรื่องนั้น)
  • procเรียกรูทีนย่อย เทียบเท่าของcall :labelเทียบเท่า
  • returnส่งคืนจากรูทีนย่อย จะออกจากโปรแกรมเมื่อไม่ได้อยู่ภายใน
  • ifคำแนะนำตามเงื่อนไข <var1> <operator> <var2>ใช้เวลาการเปรียบเทียบจากบรรทัดถัดไปในรูปแบบ ตัวดำเนินการเหมือนกับifในแบตช์คือEQU, NEQ, LSS, LEQ, GTR, GEQ. จะดำเนินการคำสั่งหลังจากนั้นก็ต่อเมื่อการเปรียบเทียบเป็นจริง
  • endif สิ้นสุดคำสั่ง if
  • catเชื่อมสองตัวแปรเข้าด้วยกัน cat a bจะเก็บค่าของ ab ใน

เมื่อไม่พบคำสั่งเหล่านี้นิพจน์จะถือว่าเป็นการกำหนดตัวแปรโดยใช้โทเค็นแรกเป็นชื่อตัวแปร $และ~ประพฤติเช่นเดียวกับในprintแต่ก็ยังมี@ตัวระบุ สิ่งนี้ถือว่าโทเค็นสุดท้ายเป็นนิพจน์ทางคณิตศาสตร์ที่ส่งผ่านไปยังset /aถือว่านี้โทเค็นที่ผ่านมาเป็นการแสดงออกทางคณิตศาสตร์ผ่านไปมันรวมถึงผู้ประกอบการส่วนใหญ่ หากไม่พบตัวระบุสามตัวนี่เป็นข้อผิดพลาดทางไวยากรณ์และตัวแปลออก

ล่าม (แบทช์ Windows)

ล่ามแปลรหัสเป็นชุดของ windows วางไว้ในไฟล์ชั่วคราวและดำเนินการ ในขณะที่มันรับรู้ข้อผิดพลาดทางไวยากรณ์ในครึ่งภาษาสคริปต์แบทช์ผลลัพธ์อาจทำให้เกิดปัญหาโดยเฉพาะอย่างยิ่งกับอักขระพิเศษเช่นวงเล็บแถบแนวตั้งเป็นต้น

@echo off

REM Half Interpreter / Translator

if exist ~~.bat del ~~.bat
if not exist "%1" call :error "File not found: '%1'"
set error=
setlocal enabledelayedexpansion
call :parse "%1" 1>~~.bat
if exist ~~.bat if not "error"=="" ~~.bat 2>nul
goto :eof

:parse
set ifstate=0
echo @echo off
echo setlocal
echo setlocal enabledelayedexpansion
for /f "eol=# tokens=1,2* delims= " %%a in (%~1) do  (
    if "!ifstate!"=="1" (
        if /i not "%%b"=="equ" if /i not "%%b"=="neq" if /i not "%%b"=="lss" if /i not "%%b"=="leq" if /i not "%%b"=="gtr" if /i not "%%b"=="geq" call :error "Unknown comparator: '%%b'"
        echo if "^!%%a^!" %%b "^!%%c^!" ^(
        set ifstate=0
    ) else (
        if "%%a"=="print" (
            if "%%b"=="$" (
                echo echo.^^!%%c^^!
            ) else if "%%b"=="~" (
                echo echo.%%~c
            ) else call :error "Unknown identifier for print: '%%b'"
        ) else if "%%a"=="write" (
            if "%%b"=="$" (
                echo echo^|set/p="^!%%c^!"
            ) else if "%%b"=="~" (
                echo echo^|set/p="%%~c"
            ) else call :error "Unknown identifier for write: '%%b'"
        ) else if "%%a"=="mark" (
            if not "%%c"=="" call :error "Unexpected token: %%c"
            echo :%%b
        ) else if "%%a"=="jump" (
            if not "%%c"=="" call :error "Unexpected token: %%c"
            echo goto :%%b
        ) else if "%%a"=="proc" (
            if not "%%c"=="" call :error "Unexpected token: %%c"
            echo call :%%b
        ) else if "%%a"=="return" (
            if not "%%c"=="" call :error "Unexpected tokens: %%b %%c"
            if not "%%b"=="" call :error "Unexpected token: %%b"
            echo goto :eof
        ) else if "%%a"=="if" (
            if not "%%c"=="" call :error "Unexpected tokens: %%b %%c"
            if not "%%b"=="" call :error "Unexpected token: %%b"
            set ifstate=1
        ) else if "%%a"=="endif" (
            if not "%%c"=="" call :error "Unexpected tokens: %%b %%c"
            if not "%%b"=="" call :error "Unexpected token: %%b"
            echo ^)
        ) else if "%%a"=="cat" (
            echo set "%%b=^!%%b^!^!%%c^!"
        ) else (
            if "%%b"=="$" (
                echo set "%%a=!%%c!"
            ) else if "%%b"=="~" (
                echo set "%%a=%%~c"
            ) else if "%%b"=="@" (
                echo set/a"%%a=%%c"
            ) else call :error "Unknown tokens '%%a %%b %%c'"
        )
    )
)
echo endlocal
goto :eof

:error
echo.Parse Error: %~1 1>&2
set error=1
goto :eof


4

Flex Bison

กำหนดตัวแปรถ้าบล็อกเงื่อนไขอื่นและการดำเนินการลบอื่น ๆ

ไฟล์ภาษา lex.l

%{
 #include <stdio.h>
 #include <stdlib.h>
%}

 var [A-Za-z][A-Za-z0-9]*
 digit [0-9]+
 comment \*\*[A-Za-z0-9\*\/\+\-\(\)\"\' \t;:=]*\n

 %%
 print {return(PRINT);}
 save {return(SAVE);}
 {digit} {yylval=atoi(yytext);return(DIGIT);}
 {var} {yylval=strdup(yytext);return(VAR);}
 \* {return(M_SIGN);}
 \/ {return(D_SIGN);}
 \+ {return(A_SIGN);}
 \- {return(S_SIGN);}
 \( {return(L_BRACE);}
 \) {return(R_BRACE);}
 = {return(E_SIGN);}
 ; {return(S_COLON);}
 : {return(COMMA);}
 \n {return (NW_LINE);}
 [ \t] /*skip*/;
 {comment} /*skip*/;
 %%

ไฟล์แยกวิเคราะห์ com.y

  %{
    #include <ctype.h>
    #include <stdio.h>
    FILE *save_p;
    int new_line=1,stack_top=0,trigger=1;
    void value_store(int);
    int check_srore(char name_var[],int);
    void error(int);

    struct store
    {
     int var_value;
     char var_name[10];
     }info[10];        

    %}

      %token PRINT SAVE S_COLON L_BRACE R_BRACE DIGIT VAR COMMA NW_LINE
      %left A_SIGN S_SIGN
      %left D_SIGN M_SIGN
      %right E_SIGN



      %%
      commands : 
         | commands command
          ;
      command : expers
        | print
        | save
        | NW_LINE{new_line++;}
          ;

           save : SAVE expr etest {fprintf(save_p,"%d\n",$2);}
            ;

           expers  : store_val equal expr etest{value_store($3);}
        ;

           print    : PRINT expr etest {printf("%d\n",$2);} 
              ;

           etest    : S_COLON
        | DIGIT {error(0);}|PRINT{error(0);}|SAVE{error(0);}
         | VAR{error(0);}|COMMA{error(0);}
         ;

           store_val : VAR {check_store($1,0);}
          ;

           expr    : expr A_SIGN expr      { $$ = $1 + $3; } 
                | expr S_SIGN expr      { $$ = $1 - $3; }
                | expr M_SIGN expr      { $$ = $1 * $3; }
                    | expr D_SIGN expr      { $$ = $1 / $3; }
                | L_BRACE expr R_BRACE  { $$ = $2; }
                | DIGIT
                | retriv_var
                ;

             equal   : E_SIGN
             ;

             retriv_var : VAR { $$=check_store($1,1); }
           ;            

        %%

        #include "lex.yy.c"

        void error(int temp)
         {
                char *err[]={
                     "Statement Missing\n",
                 "Compund Statement Missing\n",
                     "Variable need a value\n",
                     "Invalid Argument\n"  
          };
             printf("In line no.%d:\t%s",new_line,err[temp]);   
         exit(1);
        } 

      void value_store(int store_val)
      {
       stack_top--;
      info[stack_top++].var_value = store_val;
      }

   int check_store(char name_var[],int status)
   {
     int temp = 0;
   do{
    if(strcmp(info[temp].var_name,name_var)==0)
    {
   trigger=0;
       if(status)
   {  
          trigger=1;
      return (info[temp].var_value);
   }          
     }
    temp++;     
   } while(temp<stack_top);

    if(trigger)
    {    
if(status)
{
  trigger=1;
      error(2);
}
    else
strcpy(info[stack_top++].var_name,name_var);
   }
      else trigger=1;

  }

  int yyerror(const char *str)
  {
    fprintf(stderr,"error: %s\n",str);
  }


  main(int argc, char *argv[])
  {       

if(argc != 3)
{
     error(3);
}
   yyin = fopen(argv[1],"r");
   save_p = fopen(argv[2],"w");
   yyparse();
   fclose(yyin);
   fclose(yyout);
  }

รวบรวม

  1. รายการสินค้า
  2. flex lex.l
  3. กระทิง com.y
  4. gcc -o compiler com.tab.c -lfl

วิ่ง

คอมไพเลอร์ in.txt ou.txt

ใส่ไฟล์

a = 3 + (4 * 7) -9; พิมพ์; c = a + 45; พิมพ์ c;

** นี่คือความคิดเห็นบันทึก c;

** บันทึก c ในไฟล์พิมพ์ c * (+32);

ไฟล์เอาต์พุต 67


2

ล่าม

สำหรับคำแนะนำเกี่ยวกับวิธีการเรียกใช้รหัสนี้ลองดูคำตอบอื่น ๆ ของฉัน: /codegolf//a/19935/13186

เบียร์ 99 ขวด

โปรแกรม

 bottles of beer on the wall, @ bottles of beer.
Take one down and pass it around, @ bottles of beer on the wall.

@ bottle of beer on the wall.

1 bottle of beer on the wall, 1 bottle of beer.
Take one down and pass it around, no more bottles of beer on the wall.
@@@@@@@@@@@@@@

#9.{
    !#48.+

    !<#57.<#0.^<!<#57.<#1.^<!<#56.<#64.^<
    !<#56.<#0.^<!<#56.<#1.^<!<#55.<#64.^<
    !<#55.<#0.^<!<#55.<#1.^<!<#54.<#64.^<
    !<#54.<#0.^<!<#54.<#1.^<!<#53.<#64.^<
    !<#53.<#0.^<!<#53.<#1.^<!<#52.<#64.^<
    !<#52.<#0.^<!<#52.<#1.^<!<#51.<#64.^<
    !<#51.<#0.^<!<#51.<#1.^<!<#50.<#64.^<
    !<#50.<#0.^<!<#50.<#1.^<!<#49.<#64.^<
    !<#49.<#0.^<!<#49.<#1.^<!<#48.<#64.^<
    !<#48.<#0.^<!<#48.<#1.^<!#1.-<#57.<#64.^<
    _
?}

#57.<#0.^<#57.<#1.^<!<#56.<#64.^<
#56.<#0.^<#56.<#1.^<!<#55.<#64.^<
#55.<#0.^<#55.<#1.^<!<#54.<#64.^<
#54.<#0.^<#54.<#1.^<!<#53.<#64.^<
#53.<#0.^<#53.<#1.^<!<#52.<#64.^<
#52.<#0.^<#52.<#1.^<!<#51.<#64.^<
#51.<#0.^<#51.<#1.^<!<#50.<#64.^<
#50.<#0.^<#50.<#1.^<!<#49.<
#94.^<

$

2

99ISC

99ISC ใช้หน่วยความจำจำนวนเต็มเชิงขนาด หน่วยความจำถูกทำดัชนีโดยจำนวนเต็มที่ไม่เป็นลบ ค่าทั้งหมดในหน่วยความจำเริ่มต้นได้ด้วยที่อยู่ เช่น. ที่รันไทม์ที่อยู่ 0 ประกอบด้วยค่า 0 และที่อยู่ 9 มีค่า 9

99ISC มีสองคำแนะนำ ครั้งแรกที่พิมพ์ออกมาจากขวดเบียร์ 99 บนผนังประจำ ไวยากรณ์ของมันคือบรรทัดเดียวดังต่อไปนี้ การดำเนินการต่อเนื่องกับบรรทัดถัดไปในโปรแกรม

.

คำสั่งที่สองคือคำสั่ง "การลบและสาขาหากไม่เท่ากับศูนย์" ไวยากรณ์ของมันคือบรรทัดเดียวดังต่อไปนี้

x y z

xคือที่อยู่ของหมายเลขที่จะดำเนินการyเป็นที่อยู่ของจำนวนที่จะถูกลบออกและzเป็นบรรทัดถัดไปที่จะดำเนินการหากผลลัพธ์ของการลบนั้นไม่เป็นศูนย์ มิฉะนั้นการดำเนินการจะดำเนินการกับบรรทัดถัดไป

การมีอยู่ของคำสั่ง "การลบและสาขาถ้าไม่เป็นศูนย์" ทำให้ 99ISC เป็น OISC (คอมพิวเตอร์ชุดคำสั่งเดียว) และทำให้ทัวริงสมบูรณ์

นี่คือโปรแกรมที่ลบค่า 10 ค่าแรกในหน่วยความจำแล้วพิมพ์เบียร์ 99 ขวดบนรูทีน Wall

1 1 0
2 2 0
3 3 0
4 4 0
5 5 0
6 6 0
7 7 0
8 8 0
9 9 0
.

และนี่คือล่าม 99ISC ใน Python

def interpret(filename):
    mem = range(0, 10)
    print mem

    with open(filename) as f:
            lines = f.readlines()

    ptr = 0
    while ptr < len(lines):
            line = lines[ptr]

            if line.strip() == ".":
                    for i in range(99,0,-1):
                            text = str(i) + " bottles of beer on the wall, " + str(i) + " bottles of beer.\nTake one down and pass it around, " + str(i-1) + " bottles of beer on the wall.\n\n"
                            print text.replace("0", "No more")
            else:
                    toks = map(int, line.split())
                    mem[toks[0]] = (mem[toks[0]] - mem[toks[1]]) & 0xFF
                    if mem[toks[0]] != 0:
                            ptr = toks[2]
                    else:
                            ptr += 1

1
ฉันเดาว่านี่เป็นการละเมิดกฎ "แนวทางปฏิบัติที่ดีที่สุด" สำหรับคำถามเกี่ยวกับกอล์ฟ ด้วยคำสั่งการพิมพ์ทั่วไปแทนที่ "." มันจะยังคงใช้ได้ แต่ฉันจะถูกสาปถ้าฉันจะเขียนโปรแกรม 99BOB!
intx13

2 ข้อสุดท้ายต้องการงานเพิ่มอีกเล็กน้อย
philcolbourn

2

ฉันให้คุณ:

ตัวแปลชุดคำสั่งขนาดเล็ก (SISI)

ไวยากรณ์ใช้กับ BASIC และแอสเซมบลี มันมีสี่งบ: set, print, jump(ข้ามไปอย่างไม่มีเงื่อนไข) และjumpif(โกโตะเงื่อนไข) ทุกคำสั่งจะต้องนำหน้าด้วยหมายเลขบรรทัด ชนิดข้อมูลที่รองรับคือจำนวนเต็มและสตริง

ตัวแปลภาษาสามารถพบได้ใน Python 3 บน Github (sisi.py) โปรแกรมเบียร์ 99 ขวดก็มีเช่นกัน แต่ฉันจะทำซ้ำที่นี่:

10 set x 99
20 set bottles " bottles "

100 set line x + bottles
110 set line line + "of beer on the wall, "
120 set line line + x
130 set line line + bottles
135 set line line + "of beer."
140 print line

200 set x x - 1
210 set none x = 0
220 jumpif none 400
230 set multiple x > 1
240 jumpif multiple 300
250 set bottles " bottle "

300 set line "Take one down and pass it around, " + x
310 set line line + bottles
320 set line line + "of beer on the wall."
330 print line
340 print ""
350 jump 100

400 print "Take one down and pass it around, no more bottles of beer on the wall."
410 print ""
420 print "No more bottles of beer on the wall, no more bottles of beer."
430 print "Go to the store and buy some more, 99 bottles of beer on the wall."

1

ฮอปเปอร์

https://github.com/nrubin29/Pogo

method main:void
    declare(integer,i,99)
    while i > 0
        print(i "bottles of beer on the wall")
        math(i - 1) i
    end
end main

ฉันมีปัญหาเมื่อเห็นว่านี่พิมพ์เนื้อเพลง 99 ขวด
Sylwester

ครั้งแรกที่ผมประกาศจำนวนเต็มชื่อiและตั้งค่าเท่ากับ 99 จากนั้นในขณะที่ฉันมีค่ามากกว่า 0, พิมพ์และลบหนึ่งจากi bottles of beer on the wall iหากปัญหาคือว่าฉันขาดเนื้อเพลงบางส่วนฉันสามารถเพิ่มได้อีก
nrubin29

3
มีการลิงก์ไปยังเนื้อเพลง (หรือตอนนี้) ข้อสองสามข้อสุดท้ายมีความซับซ้อนกว่าเล็กน้อย
philcolbourn
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.